All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 1/4] clocksource: Add brcm,bcm6345-timer device tree binding
@ 2015-11-21 19:02 ` Simon Arlott
  0 siblings, 0 replies; 95+ messages in thread
From: Simon Arlott @ 2015-11-21 19:02 UTC (permalink / raw)
  To: devicetree, Ralf Baechle, Thomas Gleixner, Jason Cooper,
	Marc Zyngier, Kevin Cernekee, Florian Fainelli, Wim Van Sebroeck,
	Miguel Gaio, Maxime Bizon, Linux Kernel Mailing List, linux-mips,
	linux-watchdog
  Cc: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala

Add device tree binding for the BCM6345 timer. This is required for the
BCM6345 watchdog which needs to respond to one of the timer interrupts.

Signed-off-by: Simon Arlott <simon@fire.lp0.eu>
---
 .../bindings/timer/brcm,bcm6345-timer.txt          | 57 ++++++++++++++++++++++
 1 file changed, 57 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/timer/brcm,bcm6345-timer.txt

diff --git a/Documentation/devicetree/bindings/timer/brcm,bcm6345-timer.txt b/Documentation/devicetree/bindings/timer/brcm,bcm6345-timer.txt
new file mode 100644
index 0000000..2593907
--- /dev/null
+++ b/Documentation/devicetree/bindings/timer/brcm,bcm6345-timer.txt
@@ -0,0 +1,57 @@
+Broadcom BCM6345 Timer
+
+This block is a timer that is connected to one interrupt on the main interrupt
+controller and functions as a programmable interrupt controller for timer events.
+
+- 3 to 4 independent timers with their own maskable level interrupt bit (but not
+  per CPU because there is only one parent interrupt and the timers share it)
+
+- 1 watchdog timer with an unmaskable level interrupt
+
+- Contains one enable/status word pair
+
+- No atomic set/clear operations
+
+The lack of per CPU ability of timers makes them unusable as a set of
+clockevent devices, otherwise they could be attached to the remaining
+interrupts.
+
+The BCM6318 also has a separate interrupt for every timer except the watchdog.
+
+Required properties:
+
+- compatible: should be "brcm,bcm<soc>-timer", "brcm,bcm6345-timer"
+- reg: specifies the base physical address and size of the registers, excluding
+  the watchdog registers
+- interrupt-controller: identifies the node as an interrupt controller
+- #interrupt-cells: specifies the number of cells needed to encode an interrupt
+  source, should be 1.
+- interrupt-parent: specifies the phandle to the parent interrupt controller(s)
+  this one is cascaded from
+- interrupts: specifies the interrupt line(s) in the interrupt-parent controller
+  node for the main timer interrupt, followed by the individual timer interrupts
+  if present; valid values depend on the type of parent interrupt controller
+
+Example:
+
+timer: timer@0x10000080 {
+        compatible = "brcm,bcm63168-timer", "brcm,bcm6345-timer";
+        reg = <0x10000080 0x1c>;
+
+        interrupt-controller;
+        #interrupt-cells = <1>;
+
+        interrupt-parent = <&periph_intc>;
+        interrupts = <0>;
+};
+
+timer: timer@0x10000040 {
+        compatible = "brcm,bcm6318-timer", "brcm,bcm6345-timer";
+        reg = <0x10000040 0x28>;
+
+        interrupt-controller;
+        #interrupt-cells = <1>;
+
+        interrupt-parent = <&periph_intc>;
+        interrupts = <31>, <0>, <1>, <2>, <3>;
+};
-- 
2.1.4

-- 
Simon Arlott

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

* [PATCH 1/4] clocksource: Add brcm,bcm6345-timer device tree binding
@ 2015-11-21 19:02 ` Simon Arlott
  0 siblings, 0 replies; 95+ messages in thread
From: Simon Arlott @ 2015-11-21 19:02 UTC (permalink / raw)
  To: devicetree-u79uwXL29TY76Z2rM5mHXA, Ralf Baechle, Thomas Gleixner,
	Jason Cooper, Marc Zyngier, Kevin Cernekee, Florian Fainelli,
	Wim Van Sebroeck, Miguel Gaio, Maxime Bizon,
	Linux Kernel Mailing List, linux-mips-6z/3iImG2C8G8FEW9MqTrA,
	linux-watchdog-u79uwXL29TY76Z2rM5mHXA
  Cc: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala

Add device tree binding for the BCM6345 timer. This is required for the
BCM6345 watchdog which needs to respond to one of the timer interrupts.

Signed-off-by: Simon Arlott <simon-A6De1vDTPLDsq35pWSNszA@public.gmane.org>
---
 .../bindings/timer/brcm,bcm6345-timer.txt          | 57 ++++++++++++++++++++++
 1 file changed, 57 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/timer/brcm,bcm6345-timer.txt

diff --git a/Documentation/devicetree/bindings/timer/brcm,bcm6345-timer.txt b/Documentation/devicetree/bindings/timer/brcm,bcm6345-timer.txt
new file mode 100644
index 0000000..2593907
--- /dev/null
+++ b/Documentation/devicetree/bindings/timer/brcm,bcm6345-timer.txt
@@ -0,0 +1,57 @@
+Broadcom BCM6345 Timer
+
+This block is a timer that is connected to one interrupt on the main interrupt
+controller and functions as a programmable interrupt controller for timer events.
+
+- 3 to 4 independent timers with their own maskable level interrupt bit (but not
+  per CPU because there is only one parent interrupt and the timers share it)
+
+- 1 watchdog timer with an unmaskable level interrupt
+
+- Contains one enable/status word pair
+
+- No atomic set/clear operations
+
+The lack of per CPU ability of timers makes them unusable as a set of
+clockevent devices, otherwise they could be attached to the remaining
+interrupts.
+
+The BCM6318 also has a separate interrupt for every timer except the watchdog.
+
+Required properties:
+
+- compatible: should be "brcm,bcm<soc>-timer", "brcm,bcm6345-timer"
+- reg: specifies the base physical address and size of the registers, excluding
+  the watchdog registers
+- interrupt-controller: identifies the node as an interrupt controller
+- #interrupt-cells: specifies the number of cells needed to encode an interrupt
+  source, should be 1.
+- interrupt-parent: specifies the phandle to the parent interrupt controller(s)
+  this one is cascaded from
+- interrupts: specifies the interrupt line(s) in the interrupt-parent controller
+  node for the main timer interrupt, followed by the individual timer interrupts
+  if present; valid values depend on the type of parent interrupt controller
+
+Example:
+
+timer: timer@0x10000080 {
+        compatible = "brcm,bcm63168-timer", "brcm,bcm6345-timer";
+        reg = <0x10000080 0x1c>;
+
+        interrupt-controller;
+        #interrupt-cells = <1>;
+
+        interrupt-parent = <&periph_intc>;
+        interrupts = <0>;
+};
+
+timer: timer@0x10000040 {
+        compatible = "brcm,bcm6318-timer", "brcm,bcm6345-timer";
+        reg = <0x10000040 0x28>;
+
+        interrupt-controller;
+        #interrupt-cells = <1>;
+
+        interrupt-parent = <&periph_intc>;
+        interrupts = <31>, <0>, <1>, <2>, <3>;
+};
-- 
2.1.4

-- 
Simon Arlott
--
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] 95+ messages in thread

* [PATCH 2/4] MIPS: bmips: Add bcm6345-l2-timer interrupt controller
  2015-11-21 19:02 ` Simon Arlott
  (?)
@ 2015-11-21 19:03 ` Simon Arlott
  -1 siblings, 0 replies; 95+ messages in thread
From: Simon Arlott @ 2015-11-21 19:03 UTC (permalink / raw)
  To: devicetree, Ralf Baechle, Thomas Gleixner, Jason Cooper,
	Marc Zyngier, Kevin Cernekee, Florian Fainelli, Wim Van Sebroeck,
	Miguel Gaio, Maxime Bizon, Linux Kernel Mailing List, linux-mips,
	linux-watchdog
  Cc: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala

Add the BCM6345 timer as an interrupt controller so that it can be used
by the watchdog to warn that its timer will expire soon.

Support for clocksource/clockevents is not implemented as the timer
interrupt is not per CPU (except on the BCM6318) and the MIPS clock is
better. This could be added later if required without changing the device
tree binding.

Signed-off-by: Simon Arlott <simon@fire.lp0.eu>
---
 drivers/irqchip/Kconfig                |   5 +
 drivers/irqchip/Makefile               |   1 +
 drivers/irqchip/irq-bcm6345-l2-timer.c | 317 +++++++++++++++++++++++++++++++++
 3 files changed, 323 insertions(+)
 create mode 100644 drivers/irqchip/irq-bcm6345-l2-timer.c

diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
index d307bb3..21c3d9b 100644
--- a/drivers/irqchip/Kconfig
+++ b/drivers/irqchip/Kconfig
@@ -70,6 +70,11 @@ config BCM6345_L1_IRQ
 	select GENERIC_IRQ_CHIP
 	select IRQ_DOMAIN
 
+config BCM6345_L2_TIMER_IRQ
+	bool
+	select GENERIC_IRQ_CHIP
+	select IRQ_DOMAIN
+
 config BCM7038_L1_IRQ
 	bool
 	select GENERIC_IRQ_CHIP
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index ded59cf..2687dea 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -44,6 +44,7 @@ obj-$(CONFIG_XTENSA_MX)			+= irq-xtensa-mx.o
 obj-$(CONFIG_IRQ_CROSSBAR)		+= irq-crossbar.o
 obj-$(CONFIG_SOC_VF610)			+= irq-vf610-mscm-ir.o
 obj-$(CONFIG_BCM6345_L1_IRQ)		+= irq-bcm6345-l1.o
+obj-$(CONFIG_BCM6345_L2_TIMER_IRQ)	+= irq-bcm6345-l2-timer.o
 obj-$(CONFIG_BCM7038_L1_IRQ)		+= irq-bcm7038-l1.o
 obj-$(CONFIG_BCM7120_L2_IRQ)		+= irq-bcm7120-l2.o
 obj-$(CONFIG_BRCMSTB_L2_IRQ)		+= irq-brcmstb-l2.o
diff --git a/drivers/irqchip/irq-bcm6345-l2-timer.c b/drivers/irqchip/irq-bcm6345-l2-timer.c
new file mode 100644
index 0000000..a388fcb
--- /dev/null
+++ b/drivers/irqchip/irq-bcm6345-l2-timer.c
@@ -0,0 +1,317 @@
+/*
+ * Copyright 2015 Simon Arlott
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * Based on arch/mips/bcm63xx/timer.c:
+ * Copyright (C) 2008 Maxime Bizon <mbizon@freebox.fr>
+ *
+ * Registers for SoCs with 4 timers: BCM6345, BCM6328, BCM6362, BCM6816,
+ *                                   BCM68220,BCM63168, BCM63268
+ *   0x02: IRQ enable (u8)
+ *   0x03: IRQ status (u8)
+ *   0x04: Timer 0 control
+ *   0x08: Timer 1 control
+ *   0x0c: Timer 2 control
+ *   0x10: Timer 0 count
+ *   0x14: Timer 1 count
+ *   0x18: Timer 2 count
+ *   0x1c+: Watchdog registers
+ *
+ * Registers for SoCs with 5 timers: BCM6318
+ *   0x00: IRQ enable (u32)
+ *   0x04: IRQ status (u32)
+ *   0x08: Timer 0 control
+ *   0x0c: Timer 1 control
+ *   0x10: Timer 2 control
+ *   0x14: Timer 3 control
+ *   0x18: Timer 0 count
+ *   0x1c: Timer 1 count
+ *   0x20: Timer 2 count
+ *   0x24: Timer 3 count
+ *   0x28+: Watchdog registers
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/bitops.h>
+#include <linux/interrupt.h>
+#include <linux/irqreturn.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/of_platform.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/string.h>
+#include <linux/irqchip.h>
+#include <linux/irqchip/chained_irq.h>
+
+#define REG_6345_IRQ_ENABLE		0x02
+#define REG_6345_IRQ_STATUS		0x03
+#define REG_6345_CFG_BASE		0x04
+
+#define REG_6318_IRQ_ENABLE		0x00
+#define REG_6318_IRQ_STATUS		0x04
+#define REG_6318_CFG_BASE		0x08
+
+#define NR_TIMERS_6345			4
+#define WDT_TIMER_ID_6345		(NR_TIMERS_6345 - 1)
+
+#define NR_TIMERS_6318			5
+#define WDT_TIMER_ID_6318		(NR_TIMERS_6318 - 1)
+
+/* Per-timer count register */
+#define COUNT_MASK			(0x3fffffff)
+
+/* Per-timer control register */
+#define CONTROL_COUNTDOWN_MASK		(0x3fffffff)
+#define CONTROL_RSTCNTCLR_MASK		(1 << 30)
+#define CONTROL_ENABLE_MASK		(1 << 31)
+
+enum bcm6345_timer_type {
+	TIMER_TYPE_6345,
+	TIMER_TYPE_6318,
+};
+
+struct bcm6345_timer {
+	raw_spinlock_t lock;
+	void __iomem *base;
+	unsigned int irq;
+	struct irq_domain *domain;
+
+	enum bcm6345_timer_type type;
+	unsigned int nr_timers;
+	/* The watchdog timer has separate control/remaining registers
+	 * and cannot be masked.
+	 */
+	int wdt_timer_id;
+};
+
+static inline u32 bcm6345_timer_read_int_status(struct bcm6345_timer *timer)
+{
+	if (timer->type == TIMER_TYPE_6318)
+		return __raw_readl(timer->base + REG_6318_IRQ_STATUS);
+	else
+		return __raw_readb(timer->base + REG_6345_IRQ_STATUS);
+}
+
+static inline void bcm6345_timer_write_int_status(struct bcm6345_timer *timer,
+	u32 val)
+{
+	if (timer->type == TIMER_TYPE_6318)
+		__raw_writel(val, timer->base + REG_6318_IRQ_STATUS);
+	else
+		__raw_writeb(val, timer->base + REG_6345_IRQ_STATUS);
+}
+
+static inline u32 bcm6345_timer_read_int_enable(struct bcm6345_timer *timer)
+{
+	if (timer->type == TIMER_TYPE_6318)
+		return __raw_readl(timer->base + REG_6318_IRQ_ENABLE);
+	else
+		return __raw_readb(timer->base + REG_6345_IRQ_ENABLE);
+}
+
+static inline void bcm6345_timer_write_int_enable(struct bcm6345_timer *timer,
+	u32 val)
+{
+	if (timer->type == TIMER_TYPE_6318)
+		__raw_writel(val, timer->base + REG_6318_IRQ_ENABLE);
+	else
+		__raw_writeb(val, timer->base + REG_6345_IRQ_ENABLE);
+}
+
+static inline void bcm6345_timer_write_control(struct bcm6345_timer *timer,
+	unsigned int id, u32 val)
+{
+	if (timer->type == TIMER_TYPE_6318)
+		__raw_writel(0, timer->base + REG_6318_CFG_BASE + id * 4);
+	else
+		__raw_writel(0, timer->base + REG_6345_CFG_BASE + id * 4);
+}
+
+static inline void bcm6345_timer_write_count(struct bcm6345_timer *timer,
+	unsigned int id, u32 val)
+{
+	/* Count registers are immediately after the control registers */
+	return bcm6345_timer_write_control(timer, timer->nr_timers + id, val);
+}
+
+static inline void bcm6345_timer_stop(struct bcm6345_timer *timer, int id)
+{
+	if (id != timer->wdt_timer_id) {
+		bcm6345_timer_write_control(timer, id, 0);
+		bcm6345_timer_write_count(timer, id, 0);
+		bcm6345_timer_write_int_status(timer, BIT(id));
+	}
+}
+
+static void bcm6345_timer_interrupt(struct irq_desc *desc)
+{
+	struct bcm6345_timer *timer = irq_desc_get_handler_data(desc);
+	struct irq_chip *chip = irq_desc_get_chip(desc);
+	unsigned long pending;
+	irq_hw_number_t hwirq;
+	unsigned int irq;
+
+	chained_irq_enter(chip, desc);
+
+	pending = bcm6345_timer_read_int_status(timer);
+	pending &= bcm6345_timer_read_int_enable(timer) |
+			BIT(timer->wdt_timer_id); /* Watchdog can't be masked */
+
+	for_each_set_bit(hwirq, &pending, timer->nr_timers) {
+		irq = irq_linear_revmap(timer->domain, hwirq);
+		if (irq)
+			do_IRQ(irq);
+		else
+			spurious_interrupt();
+	}
+
+	chained_irq_exit(chip, desc);
+}
+
+static void bcm6345_timer_unmask(struct irq_data *d)
+{
+	struct bcm6345_timer *timer = irq_data_get_irq_chip_data(d);
+	unsigned long flags;
+	u8 val;
+
+	if (d->hwirq != timer->wdt_timer_id) {
+		raw_spin_lock_irqsave(&timer->lock, flags);
+		val = bcm6345_timer_read_int_enable(timer);
+		val |= BIT(d->hwirq);
+		bcm6345_timer_write_int_enable(timer, val);
+		raw_spin_unlock_irqrestore(&timer->lock, flags);
+	}
+}
+
+static void bcm6345_timer_mask(struct irq_data *d)
+{
+	struct bcm6345_timer *timer = irq_data_get_irq_chip_data(d);
+	unsigned long flags;
+	u32 val;
+
+	if (d->hwirq != timer->wdt_timer_id) {
+		raw_spin_lock_irqsave(&timer->lock, flags);
+		val = bcm6345_timer_read_int_enable(timer);
+		val &= ~BIT(d->hwirq);
+		bcm6345_timer_write_int_enable(timer, val);
+		raw_spin_unlock_irqrestore(&timer->lock, flags);
+	}
+}
+
+static void bcm6345_timer_eoi(struct irq_data *d)
+{
+	struct bcm6345_timer *timer = irq_data_get_irq_chip_data(d);
+
+	if (d->hwirq != timer->wdt_timer_id)
+		bcm6345_timer_write_int_status(timer, BIT(d->hwirq));
+}
+
+static struct irq_chip bcm6345_timer_chip = {
+	.name			= "bcm6345-timer",
+	.irq_mask		= bcm6345_timer_mask,
+	.irq_unmask		= bcm6345_timer_unmask,
+	.irq_eoi		= bcm6345_timer_eoi,
+};
+
+static int bcm6345_timer_map(struct irq_domain *d, unsigned int virq,
+			     irq_hw_number_t hwirq)
+{
+	struct bcm6345_timer *timer = d->host_data;
+
+	irq_set_chip_and_handler(virq, &bcm6345_timer_chip,
+		hwirq == timer->wdt_timer_id ?
+			handle_simple_irq : handle_fasteoi_irq);
+	irq_set_chip_data(virq, timer);
+	return 0;
+}
+
+static const struct irq_domain_ops bcm6345_timer_domain_ops = {
+	.xlate			= irq_domain_xlate_onecell,
+	.map			= bcm6345_timer_map,
+};
+
+static int __init bcm63xx_timer_init(struct device_node *node,
+	enum bcm6345_timer_type type, unsigned int nr_timers, int wdt_timer_id)
+{
+	struct bcm6345_timer *timer;
+	int ret, i;
+
+	timer = kzalloc(sizeof(*timer), GFP_KERNEL);
+	if (!timer)
+		return -ENOMEM;
+
+	raw_spin_lock_init(&timer->lock);
+	timer->type = type;
+	timer->nr_timers = nr_timers;
+	timer->wdt_timer_id = wdt_timer_id;
+
+	timer->irq = irq_of_parse_and_map(node, 0);
+	if (!timer->irq) {
+		pr_err("unable to map parent IRQ\n");
+		ret = -EINVAL;
+		goto free_timer;
+	}
+
+	timer->base = of_iomap(node, 0);
+	if (!timer->base) {
+		pr_err("unable to remap registers\n");
+		ret = -ENOMEM;
+		goto free_timer;
+	}
+
+	timer->domain = irq_domain_add_linear(node, timer->nr_timers,
+					&bcm6345_timer_domain_ops, timer);
+	if (!timer->domain) {
+		pr_err("unable to add IRQ domain");
+		ret = -ENOMEM;
+		goto unmap_io;
+	}
+
+	/* Mask all interrupts and stop all timers */
+	bcm6345_timer_write_int_enable(timer, 0);
+	for (i = 0; i < timer->nr_timers; i++)
+		bcm6345_timer_stop(timer, i);
+
+	irq_set_chained_handler_and_data(timer->irq,
+					bcm6345_timer_interrupt, timer);
+
+	pr_info("registered BCM6345 L2 (timer) intc at MMIO 0x%p (irq = %d, IRQs: %d)\n",
+			timer->base, timer->irq, timer->nr_timers);
+	return 0;
+
+unmap_io:
+	iounmap(timer->base);
+free_timer:
+	kfree(timer);
+	return ret;
+}
+
+static int __init bcm6318_timer_init(struct device_node *node,
+				      struct device_node *parent)
+{
+	return bcm63xx_timer_init(node, TIMER_TYPE_6318,
+				NR_TIMERS_6318, WDT_TIMER_ID_6318);
+}
+
+static int __init bcm6345_timer_init(struct device_node *node,
+				      struct device_node *parent)
+{
+	return bcm63xx_timer_init(node, TIMER_TYPE_6345,
+				NR_TIMERS_6345, WDT_TIMER_ID_6345);
+}
+
+IRQCHIP_DECLARE(bcm6318_l2_timer, "brcm,bcm6318-timer", bcm6318_timer_init);
+IRQCHIP_DECLARE(bcm6345_l2_timer, "brcm,bcm6345-timer", bcm6345_timer_init);
-- 
2.1.4

-- 
Simon Arlott

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

* [PATCH 3/4] watchdog: Add brcm,bcm6345-wdt device tree binding
  2015-11-21 19:02 ` Simon Arlott
  (?)
  (?)
@ 2015-11-21 19:04 ` Simon Arlott
  2015-11-22 22:13     ` Rob Herring
  -1 siblings, 1 reply; 95+ messages in thread
From: Simon Arlott @ 2015-11-21 19:04 UTC (permalink / raw)
  To: devicetree, Ralf Baechle, Thomas Gleixner, Jason Cooper,
	Marc Zyngier, Kevin Cernekee, Florian Fainelli, Wim Van Sebroeck,
	Miguel Gaio, Maxime Bizon, Linux Kernel Mailing List, linux-mips,
	linux-watchdog
  Cc: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala

Add device tree binding for the BCM6345 watchdog.

This uses the BCM6345 timer for its warning interrupt.

Signed-off-by: Simon Arlott <simon@fire.lp0.eu>
---
 .../bindings/watchdog/brcm,bcm6345-wdt.txt         | 35 ++++++++++++++++++++++
 1 file changed, 35 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/watchdog/brcm,bcm6345-wdt.txt

diff --git a/Documentation/devicetree/bindings/watchdog/brcm,bcm6345-wdt.txt b/Documentation/devicetree/bindings/watchdog/brcm,bcm6345-wdt.txt
new file mode 100644
index 0000000..9d852d4
--- /dev/null
+++ b/Documentation/devicetree/bindings/watchdog/brcm,bcm6345-wdt.txt
@@ -0,0 +1,35 @@
+BCM6345 Watchdog timer
+
+Required properties:
+
+- compatible: should be "brcm,bcm63<soc>-wdt", "brcm,bcm6345-wdt"
+- reg: Specifies base physical address and size of the registers.
+- clocks: Specify the clock used for timing
+
+Optional properties:
+
+- interrupt-parent: phandle to the interrupt controller
+- interrupts: Specify the interrupt used for the watchdog timout warning
+- timeout-sec: Contains the default watchdog timeout in seconds
+
+Example:
+
+watchdog {
+	compatible = "brcm,bcm63168-wdt", "brcm,bcm6345-wdt";
+	reg = <0x1000009c 0x0c>;
+	clocks = <&periph_clk>;
+
+	interrupt-parent = <&timer>;
+	interrupts = <3>;
+	timeout-sec = <30>;
+};
+
+watchdog {
+	compatible = "brcm,bcm6318-wdt", "brcm,bcm6345-wdt";
+	reg = <0x10000068 0x0c>;
+	clocks = <&periph_clk>;
+
+	interrupt-parent = <&timer>;
+	interrupts = <3>;
+	timeout-sec = <30>;
+};
-- 
2.1.4

-- 
Simon Arlott

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

* [PATCH 4/4] MIPS: bmips: Convert bcm63xx_wdt to use WATCHDOG_CORE
@ 2015-11-21 19:05   ` Simon Arlott
  0 siblings, 0 replies; 95+ messages in thread
From: Simon Arlott @ 2015-11-21 19:05 UTC (permalink / raw)
  To: devicetree, Ralf Baechle, Thomas Gleixner, Jason Cooper,
	Marc Zyngier, Kevin Cernekee, Florian Fainelli, Wim Van Sebroeck,
	Miguel Gaio, Maxime Bizon, Linux Kernel Mailing List, linux-mips,
	linux-watchdog
  Cc: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala

Convert bcm63xx_wdt to use WATCHDOG_CORE and add a device tree binding.

Adds support for the time left value and provides a more effective
interrupt handler based on the watchdog warning interrupt behaviour.

This removes the unnecessary software countdown timer and replaces the
use of bcm63xx_timer with a normal interrupt when not using mach-bcm63xx.

Signed-off-by: Simon Arlott <simon@fire.lp0.eu>
---
 arch/mips/bcm63xx/prom.c                          |   1 +
 arch/mips/bcm63xx/setup.c                         |   1 +
 arch/mips/include/asm/mach-bcm63xx/bcm63xx_regs.h |  22 --
 drivers/watchdog/Kconfig                          |   4 +-
 drivers/watchdog/bcm63xx_wdt.c                    | 420 +++++++++++-----------
 include/linux/bcm63xx_wdt.h                       |  22 ++
 6 files changed, 244 insertions(+), 226 deletions(-)
 create mode 100644 include/linux/bcm63xx_wdt.h

diff --git a/arch/mips/bcm63xx/prom.c b/arch/mips/bcm63xx/prom.c
index 7019e29..ba8b354 100644
--- a/arch/mips/bcm63xx/prom.c
+++ b/arch/mips/bcm63xx/prom.c
@@ -17,6 +17,7 @@
 #include <bcm63xx_cpu.h>
 #include <bcm63xx_io.h>
 #include <bcm63xx_regs.h>
+#include <linux/bcm63xx_wdt.h>
 
 void __init prom_init(void)
 {
diff --git a/arch/mips/bcm63xx/setup.c b/arch/mips/bcm63xx/setup.c
index 240fb4f..6abf364 100644
--- a/arch/mips/bcm63xx/setup.c
+++ b/arch/mips/bcm63xx/setup.c
@@ -21,6 +21,7 @@
 #include <bcm63xx_regs.h>
 #include <bcm63xx_io.h>
 #include <bcm63xx_gpio.h>
+#include <linux/bcm63xx_wdt.h>
 
 void bcm63xx_machine_halt(void)
 {
diff --git a/arch/mips/include/asm/mach-bcm63xx/bcm63xx_regs.h b/arch/mips/include/asm/mach-bcm63xx/bcm63xx_regs.h
index 5035f09..16a745b 100644
--- a/arch/mips/include/asm/mach-bcm63xx/bcm63xx_regs.h
+++ b/arch/mips/include/asm/mach-bcm63xx/bcm63xx_regs.h
@@ -441,28 +441,6 @@
 
 
 /*************************************************************************
- * _REG relative to RSET_WDT
- *************************************************************************/
-
-/* Watchdog default count register */
-#define WDT_DEFVAL_REG			0x0
-
-/* Watchdog control register */
-#define WDT_CTL_REG			0x4
-
-/* Watchdog control register constants */
-#define WDT_START_1			(0xff00)
-#define WDT_START_2			(0x00ff)
-#define WDT_STOP_1			(0xee00)
-#define WDT_STOP_2			(0x00ee)
-
-/* Watchdog reset length register */
-#define WDT_RSTLEN_REG			0x8
-
-/* Watchdog soft reset register (BCM6328 only) */
-#define WDT_SOFTRESET_REG		0xc
-
-/*************************************************************************
  * _REG relative to RSET_GPIO
  *************************************************************************/
 
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index 7a8a6c6..0c50add 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -1272,7 +1272,9 @@ config OCTEON_WDT
 
 config BCM63XX_WDT
 	tristate "Broadcom BCM63xx hardware watchdog"
-	depends on BCM63XX
+	depends on BCM63XX || BMIPS_GENERIC
+	select WATCHDOG_CORE
+	select BCM6345_L2_TIMER_IRQ if BMIPS_GENERIC
 	help
 	  Watchdog driver for the built in watchdog hardware in Broadcom
 	  BCM63xx SoC.
diff --git a/drivers/watchdog/bcm63xx_wdt.c b/drivers/watchdog/bcm63xx_wdt.c
index ab26fd9..fff92d0 100644
--- a/drivers/watchdog/bcm63xx_wdt.c
+++ b/drivers/watchdog/bcm63xx_wdt.c
@@ -3,6 +3,7 @@
  *
  *  Copyright (C) 2007, Miguel Gaio <miguel.gaio@efixo.com>
  *  Copyright (C) 2008, Florian Fainelli <florian@openwrt.org>
+ *  Copyright 2015 Simon Arlott
  *
  *  This program is free software; you can redistribute it and/or
  *  modify it under the terms of the GNU General Public License
@@ -12,235 +13,165 @@
 
 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
 
-#include <linux/bitops.h>
+#include <linux/bcm63xx_wdt.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
 #include <linux/errno.h>
-#include <linux/fs.h>
+#include <linux/interrupt.h>
 #include <linux/io.h>
 #include <linux/kernel.h>
-#include <linux/miscdevice.h>
 #include <linux/module.h>
 #include <linux/moduleparam.h>
+#include <linux/platform_device.h>
+#include <linux/resource.h>
+#include <linux/spinlock.h>
 #include <linux/types.h>
-#include <linux/uaccess.h>
 #include <linux/watchdog.h>
-#include <linux/timer.h>
-#include <linux/jiffies.h>
-#include <linux/interrupt.h>
-#include <linux/ptrace.h>
-#include <linux/resource.h>
-#include <linux/platform_device.h>
 
-#include <bcm63xx_cpu.h>
-#include <bcm63xx_io.h>
-#include <bcm63xx_regs.h>
-#include <bcm63xx_timer.h>
+#ifdef CONFIG_BCM63XX
+# include <bcm63xx_regs.h>
+# include <bcm63xx_timer.h>
+#endif
 
 #define PFX KBUILD_MODNAME
 
-#define WDT_HZ		50000000 /* Fclk */
-#define WDT_DEFAULT_TIME	30      /* seconds */
-#define WDT_MAX_TIME		256     /* seconds */
-
-static struct {
-	void __iomem *regs;
-	struct timer_list timer;
-	unsigned long inuse;
-	atomic_t ticks;
-} bcm63xx_wdt_device;
-
-static int expect_close;
-
-static int wdt_time = WDT_DEFAULT_TIME;
 static bool nowayout = WATCHDOG_NOWAYOUT;
 module_param(nowayout, bool, 0);
 MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
 	__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
 
-/* HW functions */
-static void bcm63xx_wdt_hw_start(void)
-{
-	bcm_writel(0xfffffffe, bcm63xx_wdt_device.regs + WDT_DEFVAL_REG);
-	bcm_writel(WDT_START_1, bcm63xx_wdt_device.regs + WDT_CTL_REG);
-	bcm_writel(WDT_START_2, bcm63xx_wdt_device.regs + WDT_CTL_REG);
-}
-
-static void bcm63xx_wdt_hw_stop(void)
-{
-	bcm_writel(WDT_STOP_1, bcm63xx_wdt_device.regs + WDT_CTL_REG);
-	bcm_writel(WDT_STOP_2, bcm63xx_wdt_device.regs + WDT_CTL_REG);
-}
-
-static void bcm63xx_wdt_isr(void *data)
-{
-	struct pt_regs *regs = get_irq_regs();
-
-	die(PFX " fire", regs);
-}
-
-static void bcm63xx_timer_tick(unsigned long unused)
-{
-	if (!atomic_dec_and_test(&bcm63xx_wdt_device.ticks)) {
-		bcm63xx_wdt_hw_start();
-		mod_timer(&bcm63xx_wdt_device.timer, jiffies + HZ);
-	} else
-		pr_crit("watchdog will restart system\n");
-}
-
-static void bcm63xx_wdt_pet(void)
-{
-	atomic_set(&bcm63xx_wdt_device.ticks, wdt_time);
-}
-
-static void bcm63xx_wdt_start(void)
-{
-	bcm63xx_wdt_pet();
-	bcm63xx_timer_tick(0);
-}
+struct bcm63xx_wdt_hw {
+	raw_spinlock_t lock;
+	void __iomem *base;
+	struct clk *clk;
+	u32 clock_hz;
+	int irq;
+	bool running;
+};
 
-static void bcm63xx_wdt_pause(void)
+static int bcm63xx_wdt_start(struct watchdog_device *wdd)
 {
-	del_timer_sync(&bcm63xx_wdt_device.timer);
-	bcm63xx_wdt_hw_stop();
+	struct bcm63xx_wdt_hw *hw = watchdog_get_drvdata(wdd);
+	unsigned long flags;
+
+	raw_spin_lock_irqsave(&hw->lock, flags);
+	__raw_writel(wdd->timeout * hw->clock_hz, hw->base + WDT_DEFVAL_REG);
+	__raw_writel(WDT_START_1, hw->base + WDT_CTL_REG);
+	__raw_writel(WDT_START_2, hw->base + WDT_CTL_REG);
+	hw->running = true;
+	raw_spin_unlock_irqrestore(&hw->lock, flags);
+	return 0;
 }
 
-static int bcm63xx_wdt_settimeout(int new_time)
+static int bcm63xx_wdt_stop(struct watchdog_device *wdd)
 {
-	if ((new_time <= 0) || (new_time > WDT_MAX_TIME))
-		return -EINVAL;
-
-	wdt_time = new_time;
-
+	struct bcm63xx_wdt_hw *hw = watchdog_get_drvdata(wdd);
+	unsigned long flags;
+
+	raw_spin_lock_irqsave(&hw->lock, flags);
+	__raw_writel(WDT_STOP_1, hw->base + WDT_CTL_REG);
+	__raw_writel(WDT_STOP_2, hw->base + WDT_CTL_REG);
+	hw->running = false;
+	raw_spin_unlock_irqrestore(&hw->lock, flags);
 	return 0;
 }
 
-static int bcm63xx_wdt_open(struct inode *inode, struct file *file)
+static unsigned int bcm63xx_wdt_get_timeleft(struct watchdog_device *wdd)
 {
-	if (test_and_set_bit(0, &bcm63xx_wdt_device.inuse))
-		return -EBUSY;
-
-	bcm63xx_wdt_start();
-	return nonseekable_open(inode, file);
+	struct bcm63xx_wdt_hw *hw = watchdog_get_drvdata(wdd);
+	unsigned long flags;
+	u32 val;
+
+	raw_spin_lock_irqsave(&hw->lock, flags);
+	val = __raw_readl(hw->base + WDT_CTL_REG);
+	val /= hw->clock_hz;
+	raw_spin_unlock_irqrestore(&hw->lock, flags);
+	return val;
 }
 
-static int bcm63xx_wdt_release(struct inode *inode, struct file *file)
+static int bcm63xx_wdt_set_timeout(struct watchdog_device *wdd,
+	unsigned int timeout)
 {
-	if (expect_close == 42)
-		bcm63xx_wdt_pause();
-	else {
-		pr_crit("Unexpected close, not stopping watchdog!\n");
-		bcm63xx_wdt_start();
-	}
-	clear_bit(0, &bcm63xx_wdt_device.inuse);
-	expect_close = 0;
-	return 0;
+	wdd->timeout = timeout;
+	return bcm63xx_wdt_start(wdd);
 }
 
-static ssize_t bcm63xx_wdt_write(struct file *file, const char *data,
-				size_t len, loff_t *ppos)
+/* The watchdog interrupt occurs when half the timeout is remaining */
+#ifdef CONFIG_BCM63XX
+static void bcm63xx_wdt_interrupt(void *data)
+#else
+static irqreturn_t bcm63xx_wdt_interrupt(int irq, void *data)
+#endif
 {
-	if (len) {
-		if (!nowayout) {
-			size_t i;
-
-			/* In case it was set long ago */
-			expect_close = 0;
-
-			for (i = 0; i != len; i++) {
-				char c;
-				if (get_user(c, data + i))
-					return -EFAULT;
-				if (c == 'V')
-					expect_close = 42;
-			}
+	struct watchdog_device *wdd = data;
+	struct bcm63xx_wdt_hw *hw = watchdog_get_drvdata(wdd);
+	unsigned long flags;
+
+	raw_spin_lock_irqsave(&hw->lock, flags);
+	if (!hw->running) {
+		/* Oops */
+		__raw_writel(WDT_STOP_1, hw->base + WDT_CTL_REG);
+		__raw_writel(WDT_STOP_2, hw->base + WDT_CTL_REG);
+	} else {
+		u32 timeleft = __raw_readl(hw->base + WDT_CTL_REG);
+		u32 ms;
+
+		if (timeleft >= 2) {
+			/* The only way to stop this interrupt without masking
+			 * the whole timer interrupt or disrupting the intended
+			 * behaviour of the watchdog is to restart the watchdog
+			 * with the remaining time value so that the interrupt
+			 * occurs again at 1/4th, 1/8th, etc. of the timeout
+			 * until we reboot.
+			 *
+			 * This is done with a lock held in case userspace is
+			 * restarting the watchdog on another CPU.
+			 */
+			__raw_writel(timeleft, hw->base + WDT_DEFVAL_REG);
+			__raw_writel(WDT_START_1, hw->base + WDT_CTL_REG);
+			__raw_writel(WDT_START_2, hw->base + WDT_CTL_REG);
+		} else {
+			/* The watchdog cannot be started with a time of less
+			 * than 2 ticks (it won't fire).
+			 */
+			die(PFX ": watchdog timer expired\n", get_irq_regs());
 		}
-		bcm63xx_wdt_pet();
-	}
-	return len;
-}
-
-static struct watchdog_info bcm63xx_wdt_info = {
-	.identity       = PFX,
-	.options        = WDIOF_SETTIMEOUT |
-				WDIOF_KEEPALIVEPING |
-				WDIOF_MAGICCLOSE,
-};
-
-
-static long bcm63xx_wdt_ioctl(struct file *file, unsigned int cmd,
-				unsigned long arg)
-{
-	void __user *argp = (void __user *)arg;
-	int __user *p = argp;
-	int new_value, retval = -EINVAL;
-
-	switch (cmd) {
-	case WDIOC_GETSUPPORT:
-		return copy_to_user(argp, &bcm63xx_wdt_info,
-			sizeof(bcm63xx_wdt_info)) ? -EFAULT : 0;
-
-	case WDIOC_GETSTATUS:
-	case WDIOC_GETBOOTSTATUS:
-		return put_user(0, p);
-
-	case WDIOC_SETOPTIONS:
-		if (get_user(new_value, p))
-			return -EFAULT;
-
-		if (new_value & WDIOS_DISABLECARD) {
-			bcm63xx_wdt_pause();
-			retval = 0;
-		}
-		if (new_value & WDIOS_ENABLECARD) {
-			bcm63xx_wdt_start();
-			retval = 0;
-		}
-
-		return retval;
-
-	case WDIOC_KEEPALIVE:
-		bcm63xx_wdt_pet();
-		return 0;
-
-	case WDIOC_SETTIMEOUT:
-		if (get_user(new_value, p))
-			return -EFAULT;
-
-		if (bcm63xx_wdt_settimeout(new_value))
-			return -EINVAL;
-
-		bcm63xx_wdt_pet();
-
-	case WDIOC_GETTIMEOUT:
-		return put_user(wdt_time, p);
-
-	default:
-		return -ENOTTY;
 
+		ms = timeleft / (hw->clock_hz / 1000);
+		dev_alert(wdd->dev, "warning timer fired, reboot in %ums", ms);
 	}
+	raw_spin_unlock_irqrestore(&hw->lock, flags);
+#ifndef CONFIG_BCM63XX
+	return IRQ_HANDLED;
+#endif
 }
 
-static const struct file_operations bcm63xx_wdt_fops = {
-	.owner		= THIS_MODULE,
-	.llseek		= no_llseek,
-	.write		= bcm63xx_wdt_write,
-	.unlocked_ioctl	= bcm63xx_wdt_ioctl,
-	.open		= bcm63xx_wdt_open,
-	.release	= bcm63xx_wdt_release,
+static struct watchdog_ops bcm63xx_wdt_ops = {
+	.owner = THIS_MODULE,
+	.start = bcm63xx_wdt_start,
+	.stop = bcm63xx_wdt_stop,
+	.get_timeleft = bcm63xx_wdt_get_timeleft,
+	.set_timeout = bcm63xx_wdt_set_timeout,
 };
 
-static struct miscdevice bcm63xx_wdt_miscdev = {
-	.minor	= WATCHDOG_MINOR,
-	.name	= "watchdog",
-	.fops	= &bcm63xx_wdt_fops,
+static const struct watchdog_info bcm63xx_wdt_info = {
+	.options = WDIOC_GETTIMELEFT | WDIOF_SETTIMEOUT |
+			WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
+	.identity = "BCM63xx Watchdog",
 };
 
-
 static int bcm63xx_wdt_probe(struct platform_device *pdev)
 {
-	int ret;
+	struct bcm63xx_wdt_hw *hw;
+	struct watchdog_device *wdd;
 	struct resource *r;
+	unsigned int timeleft;
+	int ret;
 
-	setup_timer(&bcm63xx_wdt_device.timer, bcm63xx_timer_tick, 0L);
+	hw = devm_kzalloc(&pdev->dev, sizeof(*hw), GFP_KERNEL);
+	wdd = devm_kzalloc(&pdev->dev, sizeof(*wdd), GFP_KERNEL);
+	if (!hw || !wdd)
+		return -ENOMEM;
 
 	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 	if (!r) {
@@ -248,63 +179,145 @@ static int bcm63xx_wdt_probe(struct platform_device *pdev)
 		return -ENODEV;
 	}
 
-	bcm63xx_wdt_device.regs = devm_ioremap_nocache(&pdev->dev, r->start,
-							resource_size(r));
-	if (!bcm63xx_wdt_device.regs) {
+	hw->base = devm_ioremap_nocache(&pdev->dev, r->start, resource_size(r));
+	if (!hw->base) {
 		dev_err(&pdev->dev, "failed to remap I/O resources\n");
 		return -ENXIO;
 	}
 
-	ret = bcm63xx_timer_register(TIMER_WDT_ID, bcm63xx_wdt_isr, NULL);
-	if (ret < 0) {
-		dev_err(&pdev->dev, "failed to register wdt timer isr\n");
+#ifdef CONFIG_BCM63XX
+	hw->clk = devm_clk_get(&pdev->dev, "periph");
+#else
+	hw->clk = devm_clk_get(&pdev->dev, NULL);
+#endif
+	if (IS_ERR(hw->clk)) {
+		dev_err(&pdev->dev, "unable to request clock\n");
+		return PTR_ERR(hw->clk);
+	}
+
+	hw->clock_hz = clk_get_rate(hw->clk);
+	if (!hw->clock_hz) {
+		dev_err(&pdev->dev, "unable to fetch clock rate\n");
+		return -EINVAL;
+	}
+
+	ret = clk_prepare_enable(hw->clk);
+	if (ret) {
+		dev_err(&pdev->dev, "unable to enable clock\n");
 		return ret;
 	}
 
-	if (bcm63xx_wdt_settimeout(wdt_time)) {
-		bcm63xx_wdt_settimeout(WDT_DEFAULT_TIME);
-		dev_info(&pdev->dev,
-			": wdt_time value must be 1 <= wdt_time <= 256, using %d\n",
-			wdt_time);
+	raw_spin_lock_init(&hw->lock);
+	hw->running = false;
+
+	wdd->parent = &pdev->dev;
+	wdd->ops = &bcm63xx_wdt_ops;
+	wdd->info = &bcm63xx_wdt_info;
+	wdd->min_timeout = 1;
+	wdd->max_timeout = 0xffffffff / hw->clock_hz;
+	wdd->timeout = min(30U, wdd->max_timeout);
+
+	watchdog_set_drvdata(wdd, hw);
+	platform_set_drvdata(pdev, wdd);
+
+	watchdog_init_timeout(wdd, 0, &pdev->dev);
+	watchdog_set_nowayout(wdd, nowayout);
+
+	timeleft = bcm63xx_wdt_get_timeleft(wdd);
+	if (timeleft > 0)
+		hw->running = true;
+
+#ifdef CONFIG_BCM63XX
+	ret = bcm63xx_timer_register(TIMER_WDT_ID, bcm63xx_wdt_interrupt, wdd);
+	if (ret) {
+		dev_err(&pdev->dev, "failed to register with bcm63xx_timer\n");
+		goto disable_clk;
 	}
+	hw->irq = 0;
+#endif
 
-	ret = misc_register(&bcm63xx_wdt_miscdev);
+	ret = watchdog_register_device(wdd);
 	if (ret < 0) {
 		dev_err(&pdev->dev, "failed to register watchdog device\n");
+#ifdef CONFIG_BCM63XX
 		goto unregister_timer;
+#else
+		goto disable_clk;
+#endif
 	}
 
-	dev_info(&pdev->dev, " started, timer margin: %d sec\n",
-						WDT_DEFAULT_TIME);
+#ifndef CONFIG_BCM63XX
+	hw->irq = platform_get_irq(pdev, 0);
+	if (hw->irq) {
+		ret = devm_request_irq(&pdev->dev, hw->irq,
+			bcm63xx_wdt_interrupt, IRQF_TIMER,
+			dev_name(&pdev->dev), wdd);
+		if (ret)
+			hw->irq = 0;
+	}
+#endif
+
+	if (hw->irq) {
+		dev_info(&pdev->dev,
+			"%s at MMIO 0x%p (irq = %d, timeout = %us, max_timeout = %us)",
+			dev_name(wdd->dev), hw->base, hw->irq,
+			wdd->timeout, wdd->max_timeout);
+	} else {
+		dev_info(&pdev->dev,
+			"%s at MMIO 0x%p (timeout = %us, max_timeout = %us)",
+			dev_name(wdd->dev), hw->base,
+			wdd->timeout, wdd->max_timeout);
+	}
 
+	if (timeleft > 0)
+		dev_alert(wdd->dev, "running, reboot in %us\n", timeleft);
 	return 0;
 
+#ifdef CONFIG_BCM63XX
 unregister_timer:
 	bcm63xx_timer_unregister(TIMER_WDT_ID);
+#endif
+disable_clk:
+	clk_disable(hw->clk);
 	return ret;
 }
 
 static int bcm63xx_wdt_remove(struct platform_device *pdev)
 {
-	if (!nowayout)
-		bcm63xx_wdt_pause();
+	struct watchdog_device *wdd = platform_get_drvdata(pdev);
+	struct bcm63xx_wdt_hw *hw = watchdog_get_drvdata(wdd);
 
-	misc_deregister(&bcm63xx_wdt_miscdev);
+	if (hw->irq)
+		devm_free_irq(&pdev->dev, hw->irq, wdd);
+
+#ifdef CONFIG_BCM63XX
 	bcm63xx_timer_unregister(TIMER_WDT_ID);
+#endif
+	watchdog_unregister_device(wdd);
+	clk_disable(hw->clk);
 	return 0;
 }
 
 static void bcm63xx_wdt_shutdown(struct platform_device *pdev)
 {
-	bcm63xx_wdt_pause();
+	struct watchdog_device *wdd = platform_get_drvdata(pdev);
+
+	bcm63xx_wdt_stop(wdd);
 }
 
+static const struct of_device_id bcm63xx_wdt_dt_ids[] = {
+	{ .compatible = "brcm,bcm6345-wdt" },
+	{}
+};
+MODULE_DEVICE_TABLE(of, bcm63xx_wdt_dt_ids);
+
 static struct platform_driver bcm63xx_wdt_driver = {
 	.probe	= bcm63xx_wdt_probe,
 	.remove = bcm63xx_wdt_remove,
 	.shutdown = bcm63xx_wdt_shutdown,
 	.driver = {
 		.name = "bcm63xx-wdt",
+		.of_match_table = bcm63xx_wdt_dt_ids,
 	}
 };
 
@@ -312,6 +325,7 @@ module_platform_driver(bcm63xx_wdt_driver);
 
 MODULE_AUTHOR("Miguel Gaio <miguel.gaio@efixo.com>");
 MODULE_AUTHOR("Florian Fainelli <florian@openwrt.org>");
+MODULE_AUTHOR("Simon Arlott");
 MODULE_DESCRIPTION("Driver for the Broadcom BCM63xx SoC watchdog");
 MODULE_LICENSE("GPL");
 MODULE_ALIAS("platform:bcm63xx-wdt");
diff --git a/include/linux/bcm63xx_wdt.h b/include/linux/bcm63xx_wdt.h
new file mode 100644
index 0000000..ef4792e
--- /dev/null
+++ b/include/linux/bcm63xx_wdt.h
@@ -0,0 +1,22 @@
+#ifndef LINUX_BCM63XX_WDT_H_
+#define LINUX_BCM63XX_WDT_H_
+
+/* Watchdog default count register */
+#define WDT_DEFVAL_REG			0x0
+
+/* Watchdog control register */
+#define WDT_CTL_REG			0x4
+
+/* Watchdog control register constants */
+#define WDT_START_1			(0xff00)
+#define WDT_START_2			(0x00ff)
+#define WDT_STOP_1			(0xee00)
+#define WDT_STOP_2			(0x00ee)
+
+/* Watchdog reset length register (in clock ticks) */
+#define WDT_RSTLEN_REG			0x8
+
+/* Watchdog soft reset register (BCM6328 only) */
+#define WDT_SOFTRESET_REG		0xc
+
+#endif
-- 
2.1.4

-- 
Simon Arlott

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

* [PATCH 4/4] MIPS: bmips: Convert bcm63xx_wdt to use WATCHDOG_CORE
@ 2015-11-21 19:05   ` Simon Arlott
  0 siblings, 0 replies; 95+ messages in thread
From: Simon Arlott @ 2015-11-21 19:05 UTC (permalink / raw)
  To: devicetree-u79uwXL29TY76Z2rM5mHXA, Ralf Baechle, Thomas Gleixner,
	Jason Cooper, Marc Zyngier, Kevin Cernekee, Florian Fainelli,
	Wim Van Sebroeck, Miguel Gaio, Maxime Bizon,
	Linux Kernel Mailing List, linux-mips-6z/3iImG2C8G8FEW9MqTrA,
	linux-watchdog-u79uwXL29TY76Z2rM5mHXA
  Cc: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala

Convert bcm63xx_wdt to use WATCHDOG_CORE and add a device tree binding.

Adds support for the time left value and provides a more effective
interrupt handler based on the watchdog warning interrupt behaviour.

This removes the unnecessary software countdown timer and replaces the
use of bcm63xx_timer with a normal interrupt when not using mach-bcm63xx.

Signed-off-by: Simon Arlott <simon-A6De1vDTPLDsq35pWSNszA@public.gmane.org>
---
 arch/mips/bcm63xx/prom.c                          |   1 +
 arch/mips/bcm63xx/setup.c                         |   1 +
 arch/mips/include/asm/mach-bcm63xx/bcm63xx_regs.h |  22 --
 drivers/watchdog/Kconfig                          |   4 +-
 drivers/watchdog/bcm63xx_wdt.c                    | 420 +++++++++++-----------
 include/linux/bcm63xx_wdt.h                       |  22 ++
 6 files changed, 244 insertions(+), 226 deletions(-)
 create mode 100644 include/linux/bcm63xx_wdt.h

diff --git a/arch/mips/bcm63xx/prom.c b/arch/mips/bcm63xx/prom.c
index 7019e29..ba8b354 100644
--- a/arch/mips/bcm63xx/prom.c
+++ b/arch/mips/bcm63xx/prom.c
@@ -17,6 +17,7 @@
 #include <bcm63xx_cpu.h>
 #include <bcm63xx_io.h>
 #include <bcm63xx_regs.h>
+#include <linux/bcm63xx_wdt.h>
 
 void __init prom_init(void)
 {
diff --git a/arch/mips/bcm63xx/setup.c b/arch/mips/bcm63xx/setup.c
index 240fb4f..6abf364 100644
--- a/arch/mips/bcm63xx/setup.c
+++ b/arch/mips/bcm63xx/setup.c
@@ -21,6 +21,7 @@
 #include <bcm63xx_regs.h>
 #include <bcm63xx_io.h>
 #include <bcm63xx_gpio.h>
+#include <linux/bcm63xx_wdt.h>
 
 void bcm63xx_machine_halt(void)
 {
diff --git a/arch/mips/include/asm/mach-bcm63xx/bcm63xx_regs.h b/arch/mips/include/asm/mach-bcm63xx/bcm63xx_regs.h
index 5035f09..16a745b 100644
--- a/arch/mips/include/asm/mach-bcm63xx/bcm63xx_regs.h
+++ b/arch/mips/include/asm/mach-bcm63xx/bcm63xx_regs.h
@@ -441,28 +441,6 @@
 
 
 /*************************************************************************
- * _REG relative to RSET_WDT
- *************************************************************************/
-
-/* Watchdog default count register */
-#define WDT_DEFVAL_REG			0x0
-
-/* Watchdog control register */
-#define WDT_CTL_REG			0x4
-
-/* Watchdog control register constants */
-#define WDT_START_1			(0xff00)
-#define WDT_START_2			(0x00ff)
-#define WDT_STOP_1			(0xee00)
-#define WDT_STOP_2			(0x00ee)
-
-/* Watchdog reset length register */
-#define WDT_RSTLEN_REG			0x8
-
-/* Watchdog soft reset register (BCM6328 only) */
-#define WDT_SOFTRESET_REG		0xc
-
-/*************************************************************************
  * _REG relative to RSET_GPIO
  *************************************************************************/
 
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index 7a8a6c6..0c50add 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -1272,7 +1272,9 @@ config OCTEON_WDT
 
 config BCM63XX_WDT
 	tristate "Broadcom BCM63xx hardware watchdog"
-	depends on BCM63XX
+	depends on BCM63XX || BMIPS_GENERIC
+	select WATCHDOG_CORE
+	select BCM6345_L2_TIMER_IRQ if BMIPS_GENERIC
 	help
 	  Watchdog driver for the built in watchdog hardware in Broadcom
 	  BCM63xx SoC.
diff --git a/drivers/watchdog/bcm63xx_wdt.c b/drivers/watchdog/bcm63xx_wdt.c
index ab26fd9..fff92d0 100644
--- a/drivers/watchdog/bcm63xx_wdt.c
+++ b/drivers/watchdog/bcm63xx_wdt.c
@@ -3,6 +3,7 @@
  *
  *  Copyright (C) 2007, Miguel Gaio <miguel.gaio-HH44TBFINEIAvxtiuMwx3w@public.gmane.org>
  *  Copyright (C) 2008, Florian Fainelli <florian-p3rKhJxN3npAfugRpC6u6w@public.gmane.org>
+ *  Copyright 2015 Simon Arlott
  *
  *  This program is free software; you can redistribute it and/or
  *  modify it under the terms of the GNU General Public License
@@ -12,235 +13,165 @@
 
 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
 
-#include <linux/bitops.h>
+#include <linux/bcm63xx_wdt.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
 #include <linux/errno.h>
-#include <linux/fs.h>
+#include <linux/interrupt.h>
 #include <linux/io.h>
 #include <linux/kernel.h>
-#include <linux/miscdevice.h>
 #include <linux/module.h>
 #include <linux/moduleparam.h>
+#include <linux/platform_device.h>
+#include <linux/resource.h>
+#include <linux/spinlock.h>
 #include <linux/types.h>
-#include <linux/uaccess.h>
 #include <linux/watchdog.h>
-#include <linux/timer.h>
-#include <linux/jiffies.h>
-#include <linux/interrupt.h>
-#include <linux/ptrace.h>
-#include <linux/resource.h>
-#include <linux/platform_device.h>
 
-#include <bcm63xx_cpu.h>
-#include <bcm63xx_io.h>
-#include <bcm63xx_regs.h>
-#include <bcm63xx_timer.h>
+#ifdef CONFIG_BCM63XX
+# include <bcm63xx_regs.h>
+# include <bcm63xx_timer.h>
+#endif
 
 #define PFX KBUILD_MODNAME
 
-#define WDT_HZ		50000000 /* Fclk */
-#define WDT_DEFAULT_TIME	30      /* seconds */
-#define WDT_MAX_TIME		256     /* seconds */
-
-static struct {
-	void __iomem *regs;
-	struct timer_list timer;
-	unsigned long inuse;
-	atomic_t ticks;
-} bcm63xx_wdt_device;
-
-static int expect_close;
-
-static int wdt_time = WDT_DEFAULT_TIME;
 static bool nowayout = WATCHDOG_NOWAYOUT;
 module_param(nowayout, bool, 0);
 MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
 	__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
 
-/* HW functions */
-static void bcm63xx_wdt_hw_start(void)
-{
-	bcm_writel(0xfffffffe, bcm63xx_wdt_device.regs + WDT_DEFVAL_REG);
-	bcm_writel(WDT_START_1, bcm63xx_wdt_device.regs + WDT_CTL_REG);
-	bcm_writel(WDT_START_2, bcm63xx_wdt_device.regs + WDT_CTL_REG);
-}
-
-static void bcm63xx_wdt_hw_stop(void)
-{
-	bcm_writel(WDT_STOP_1, bcm63xx_wdt_device.regs + WDT_CTL_REG);
-	bcm_writel(WDT_STOP_2, bcm63xx_wdt_device.regs + WDT_CTL_REG);
-}
-
-static void bcm63xx_wdt_isr(void *data)
-{
-	struct pt_regs *regs = get_irq_regs();
-
-	die(PFX " fire", regs);
-}
-
-static void bcm63xx_timer_tick(unsigned long unused)
-{
-	if (!atomic_dec_and_test(&bcm63xx_wdt_device.ticks)) {
-		bcm63xx_wdt_hw_start();
-		mod_timer(&bcm63xx_wdt_device.timer, jiffies + HZ);
-	} else
-		pr_crit("watchdog will restart system\n");
-}
-
-static void bcm63xx_wdt_pet(void)
-{
-	atomic_set(&bcm63xx_wdt_device.ticks, wdt_time);
-}
-
-static void bcm63xx_wdt_start(void)
-{
-	bcm63xx_wdt_pet();
-	bcm63xx_timer_tick(0);
-}
+struct bcm63xx_wdt_hw {
+	raw_spinlock_t lock;
+	void __iomem *base;
+	struct clk *clk;
+	u32 clock_hz;
+	int irq;
+	bool running;
+};
 
-static void bcm63xx_wdt_pause(void)
+static int bcm63xx_wdt_start(struct watchdog_device *wdd)
 {
-	del_timer_sync(&bcm63xx_wdt_device.timer);
-	bcm63xx_wdt_hw_stop();
+	struct bcm63xx_wdt_hw *hw = watchdog_get_drvdata(wdd);
+	unsigned long flags;
+
+	raw_spin_lock_irqsave(&hw->lock, flags);
+	__raw_writel(wdd->timeout * hw->clock_hz, hw->base + WDT_DEFVAL_REG);
+	__raw_writel(WDT_START_1, hw->base + WDT_CTL_REG);
+	__raw_writel(WDT_START_2, hw->base + WDT_CTL_REG);
+	hw->running = true;
+	raw_spin_unlock_irqrestore(&hw->lock, flags);
+	return 0;
 }
 
-static int bcm63xx_wdt_settimeout(int new_time)
+static int bcm63xx_wdt_stop(struct watchdog_device *wdd)
 {
-	if ((new_time <= 0) || (new_time > WDT_MAX_TIME))
-		return -EINVAL;
-
-	wdt_time = new_time;
-
+	struct bcm63xx_wdt_hw *hw = watchdog_get_drvdata(wdd);
+	unsigned long flags;
+
+	raw_spin_lock_irqsave(&hw->lock, flags);
+	__raw_writel(WDT_STOP_1, hw->base + WDT_CTL_REG);
+	__raw_writel(WDT_STOP_2, hw->base + WDT_CTL_REG);
+	hw->running = false;
+	raw_spin_unlock_irqrestore(&hw->lock, flags);
 	return 0;
 }
 
-static int bcm63xx_wdt_open(struct inode *inode, struct file *file)
+static unsigned int bcm63xx_wdt_get_timeleft(struct watchdog_device *wdd)
 {
-	if (test_and_set_bit(0, &bcm63xx_wdt_device.inuse))
-		return -EBUSY;
-
-	bcm63xx_wdt_start();
-	return nonseekable_open(inode, file);
+	struct bcm63xx_wdt_hw *hw = watchdog_get_drvdata(wdd);
+	unsigned long flags;
+	u32 val;
+
+	raw_spin_lock_irqsave(&hw->lock, flags);
+	val = __raw_readl(hw->base + WDT_CTL_REG);
+	val /= hw->clock_hz;
+	raw_spin_unlock_irqrestore(&hw->lock, flags);
+	return val;
 }
 
-static int bcm63xx_wdt_release(struct inode *inode, struct file *file)
+static int bcm63xx_wdt_set_timeout(struct watchdog_device *wdd,
+	unsigned int timeout)
 {
-	if (expect_close == 42)
-		bcm63xx_wdt_pause();
-	else {
-		pr_crit("Unexpected close, not stopping watchdog!\n");
-		bcm63xx_wdt_start();
-	}
-	clear_bit(0, &bcm63xx_wdt_device.inuse);
-	expect_close = 0;
-	return 0;
+	wdd->timeout = timeout;
+	return bcm63xx_wdt_start(wdd);
 }
 
-static ssize_t bcm63xx_wdt_write(struct file *file, const char *data,
-				size_t len, loff_t *ppos)
+/* The watchdog interrupt occurs when half the timeout is remaining */
+#ifdef CONFIG_BCM63XX
+static void bcm63xx_wdt_interrupt(void *data)
+#else
+static irqreturn_t bcm63xx_wdt_interrupt(int irq, void *data)
+#endif
 {
-	if (len) {
-		if (!nowayout) {
-			size_t i;
-
-			/* In case it was set long ago */
-			expect_close = 0;
-
-			for (i = 0; i != len; i++) {
-				char c;
-				if (get_user(c, data + i))
-					return -EFAULT;
-				if (c == 'V')
-					expect_close = 42;
-			}
+	struct watchdog_device *wdd = data;
+	struct bcm63xx_wdt_hw *hw = watchdog_get_drvdata(wdd);
+	unsigned long flags;
+
+	raw_spin_lock_irqsave(&hw->lock, flags);
+	if (!hw->running) {
+		/* Oops */
+		__raw_writel(WDT_STOP_1, hw->base + WDT_CTL_REG);
+		__raw_writel(WDT_STOP_2, hw->base + WDT_CTL_REG);
+	} else {
+		u32 timeleft = __raw_readl(hw->base + WDT_CTL_REG);
+		u32 ms;
+
+		if (timeleft >= 2) {
+			/* The only way to stop this interrupt without masking
+			 * the whole timer interrupt or disrupting the intended
+			 * behaviour of the watchdog is to restart the watchdog
+			 * with the remaining time value so that the interrupt
+			 * occurs again at 1/4th, 1/8th, etc. of the timeout
+			 * until we reboot.
+			 *
+			 * This is done with a lock held in case userspace is
+			 * restarting the watchdog on another CPU.
+			 */
+			__raw_writel(timeleft, hw->base + WDT_DEFVAL_REG);
+			__raw_writel(WDT_START_1, hw->base + WDT_CTL_REG);
+			__raw_writel(WDT_START_2, hw->base + WDT_CTL_REG);
+		} else {
+			/* The watchdog cannot be started with a time of less
+			 * than 2 ticks (it won't fire).
+			 */
+			die(PFX ": watchdog timer expired\n", get_irq_regs());
 		}
-		bcm63xx_wdt_pet();
-	}
-	return len;
-}
-
-static struct watchdog_info bcm63xx_wdt_info = {
-	.identity       = PFX,
-	.options        = WDIOF_SETTIMEOUT |
-				WDIOF_KEEPALIVEPING |
-				WDIOF_MAGICCLOSE,
-};
-
-
-static long bcm63xx_wdt_ioctl(struct file *file, unsigned int cmd,
-				unsigned long arg)
-{
-	void __user *argp = (void __user *)arg;
-	int __user *p = argp;
-	int new_value, retval = -EINVAL;
-
-	switch (cmd) {
-	case WDIOC_GETSUPPORT:
-		return copy_to_user(argp, &bcm63xx_wdt_info,
-			sizeof(bcm63xx_wdt_info)) ? -EFAULT : 0;
-
-	case WDIOC_GETSTATUS:
-	case WDIOC_GETBOOTSTATUS:
-		return put_user(0, p);
-
-	case WDIOC_SETOPTIONS:
-		if (get_user(new_value, p))
-			return -EFAULT;
-
-		if (new_value & WDIOS_DISABLECARD) {
-			bcm63xx_wdt_pause();
-			retval = 0;
-		}
-		if (new_value & WDIOS_ENABLECARD) {
-			bcm63xx_wdt_start();
-			retval = 0;
-		}
-
-		return retval;
-
-	case WDIOC_KEEPALIVE:
-		bcm63xx_wdt_pet();
-		return 0;
-
-	case WDIOC_SETTIMEOUT:
-		if (get_user(new_value, p))
-			return -EFAULT;
-
-		if (bcm63xx_wdt_settimeout(new_value))
-			return -EINVAL;
-
-		bcm63xx_wdt_pet();
-
-	case WDIOC_GETTIMEOUT:
-		return put_user(wdt_time, p);
-
-	default:
-		return -ENOTTY;
 
+		ms = timeleft / (hw->clock_hz / 1000);
+		dev_alert(wdd->dev, "warning timer fired, reboot in %ums", ms);
 	}
+	raw_spin_unlock_irqrestore(&hw->lock, flags);
+#ifndef CONFIG_BCM63XX
+	return IRQ_HANDLED;
+#endif
 }
 
-static const struct file_operations bcm63xx_wdt_fops = {
-	.owner		= THIS_MODULE,
-	.llseek		= no_llseek,
-	.write		= bcm63xx_wdt_write,
-	.unlocked_ioctl	= bcm63xx_wdt_ioctl,
-	.open		= bcm63xx_wdt_open,
-	.release	= bcm63xx_wdt_release,
+static struct watchdog_ops bcm63xx_wdt_ops = {
+	.owner = THIS_MODULE,
+	.start = bcm63xx_wdt_start,
+	.stop = bcm63xx_wdt_stop,
+	.get_timeleft = bcm63xx_wdt_get_timeleft,
+	.set_timeout = bcm63xx_wdt_set_timeout,
 };
 
-static struct miscdevice bcm63xx_wdt_miscdev = {
-	.minor	= WATCHDOG_MINOR,
-	.name	= "watchdog",
-	.fops	= &bcm63xx_wdt_fops,
+static const struct watchdog_info bcm63xx_wdt_info = {
+	.options = WDIOC_GETTIMELEFT | WDIOF_SETTIMEOUT |
+			WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
+	.identity = "BCM63xx Watchdog",
 };
 
-
 static int bcm63xx_wdt_probe(struct platform_device *pdev)
 {
-	int ret;
+	struct bcm63xx_wdt_hw *hw;
+	struct watchdog_device *wdd;
 	struct resource *r;
+	unsigned int timeleft;
+	int ret;
 
-	setup_timer(&bcm63xx_wdt_device.timer, bcm63xx_timer_tick, 0L);
+	hw = devm_kzalloc(&pdev->dev, sizeof(*hw), GFP_KERNEL);
+	wdd = devm_kzalloc(&pdev->dev, sizeof(*wdd), GFP_KERNEL);
+	if (!hw || !wdd)
+		return -ENOMEM;
 
 	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 	if (!r) {
@@ -248,63 +179,145 @@ static int bcm63xx_wdt_probe(struct platform_device *pdev)
 		return -ENODEV;
 	}
 
-	bcm63xx_wdt_device.regs = devm_ioremap_nocache(&pdev->dev, r->start,
-							resource_size(r));
-	if (!bcm63xx_wdt_device.regs) {
+	hw->base = devm_ioremap_nocache(&pdev->dev, r->start, resource_size(r));
+	if (!hw->base) {
 		dev_err(&pdev->dev, "failed to remap I/O resources\n");
 		return -ENXIO;
 	}
 
-	ret = bcm63xx_timer_register(TIMER_WDT_ID, bcm63xx_wdt_isr, NULL);
-	if (ret < 0) {
-		dev_err(&pdev->dev, "failed to register wdt timer isr\n");
+#ifdef CONFIG_BCM63XX
+	hw->clk = devm_clk_get(&pdev->dev, "periph");
+#else
+	hw->clk = devm_clk_get(&pdev->dev, NULL);
+#endif
+	if (IS_ERR(hw->clk)) {
+		dev_err(&pdev->dev, "unable to request clock\n");
+		return PTR_ERR(hw->clk);
+	}
+
+	hw->clock_hz = clk_get_rate(hw->clk);
+	if (!hw->clock_hz) {
+		dev_err(&pdev->dev, "unable to fetch clock rate\n");
+		return -EINVAL;
+	}
+
+	ret = clk_prepare_enable(hw->clk);
+	if (ret) {
+		dev_err(&pdev->dev, "unable to enable clock\n");
 		return ret;
 	}
 
-	if (bcm63xx_wdt_settimeout(wdt_time)) {
-		bcm63xx_wdt_settimeout(WDT_DEFAULT_TIME);
-		dev_info(&pdev->dev,
-			": wdt_time value must be 1 <= wdt_time <= 256, using %d\n",
-			wdt_time);
+	raw_spin_lock_init(&hw->lock);
+	hw->running = false;
+
+	wdd->parent = &pdev->dev;
+	wdd->ops = &bcm63xx_wdt_ops;
+	wdd->info = &bcm63xx_wdt_info;
+	wdd->min_timeout = 1;
+	wdd->max_timeout = 0xffffffff / hw->clock_hz;
+	wdd->timeout = min(30U, wdd->max_timeout);
+
+	watchdog_set_drvdata(wdd, hw);
+	platform_set_drvdata(pdev, wdd);
+
+	watchdog_init_timeout(wdd, 0, &pdev->dev);
+	watchdog_set_nowayout(wdd, nowayout);
+
+	timeleft = bcm63xx_wdt_get_timeleft(wdd);
+	if (timeleft > 0)
+		hw->running = true;
+
+#ifdef CONFIG_BCM63XX
+	ret = bcm63xx_timer_register(TIMER_WDT_ID, bcm63xx_wdt_interrupt, wdd);
+	if (ret) {
+		dev_err(&pdev->dev, "failed to register with bcm63xx_timer\n");
+		goto disable_clk;
 	}
+	hw->irq = 0;
+#endif
 
-	ret = misc_register(&bcm63xx_wdt_miscdev);
+	ret = watchdog_register_device(wdd);
 	if (ret < 0) {
 		dev_err(&pdev->dev, "failed to register watchdog device\n");
+#ifdef CONFIG_BCM63XX
 		goto unregister_timer;
+#else
+		goto disable_clk;
+#endif
 	}
 
-	dev_info(&pdev->dev, " started, timer margin: %d sec\n",
-						WDT_DEFAULT_TIME);
+#ifndef CONFIG_BCM63XX
+	hw->irq = platform_get_irq(pdev, 0);
+	if (hw->irq) {
+		ret = devm_request_irq(&pdev->dev, hw->irq,
+			bcm63xx_wdt_interrupt, IRQF_TIMER,
+			dev_name(&pdev->dev), wdd);
+		if (ret)
+			hw->irq = 0;
+	}
+#endif
+
+	if (hw->irq) {
+		dev_info(&pdev->dev,
+			"%s at MMIO 0x%p (irq = %d, timeout = %us, max_timeout = %us)",
+			dev_name(wdd->dev), hw->base, hw->irq,
+			wdd->timeout, wdd->max_timeout);
+	} else {
+		dev_info(&pdev->dev,
+			"%s at MMIO 0x%p (timeout = %us, max_timeout = %us)",
+			dev_name(wdd->dev), hw->base,
+			wdd->timeout, wdd->max_timeout);
+	}
 
+	if (timeleft > 0)
+		dev_alert(wdd->dev, "running, reboot in %us\n", timeleft);
 	return 0;
 
+#ifdef CONFIG_BCM63XX
 unregister_timer:
 	bcm63xx_timer_unregister(TIMER_WDT_ID);
+#endif
+disable_clk:
+	clk_disable(hw->clk);
 	return ret;
 }
 
 static int bcm63xx_wdt_remove(struct platform_device *pdev)
 {
-	if (!nowayout)
-		bcm63xx_wdt_pause();
+	struct watchdog_device *wdd = platform_get_drvdata(pdev);
+	struct bcm63xx_wdt_hw *hw = watchdog_get_drvdata(wdd);
 
-	misc_deregister(&bcm63xx_wdt_miscdev);
+	if (hw->irq)
+		devm_free_irq(&pdev->dev, hw->irq, wdd);
+
+#ifdef CONFIG_BCM63XX
 	bcm63xx_timer_unregister(TIMER_WDT_ID);
+#endif
+	watchdog_unregister_device(wdd);
+	clk_disable(hw->clk);
 	return 0;
 }
 
 static void bcm63xx_wdt_shutdown(struct platform_device *pdev)
 {
-	bcm63xx_wdt_pause();
+	struct watchdog_device *wdd = platform_get_drvdata(pdev);
+
+	bcm63xx_wdt_stop(wdd);
 }
 
+static const struct of_device_id bcm63xx_wdt_dt_ids[] = {
+	{ .compatible = "brcm,bcm6345-wdt" },
+	{}
+};
+MODULE_DEVICE_TABLE(of, bcm63xx_wdt_dt_ids);
+
 static struct platform_driver bcm63xx_wdt_driver = {
 	.probe	= bcm63xx_wdt_probe,
 	.remove = bcm63xx_wdt_remove,
 	.shutdown = bcm63xx_wdt_shutdown,
 	.driver = {
 		.name = "bcm63xx-wdt",
+		.of_match_table = bcm63xx_wdt_dt_ids,
 	}
 };
 
@@ -312,6 +325,7 @@ module_platform_driver(bcm63xx_wdt_driver);
 
 MODULE_AUTHOR("Miguel Gaio <miguel.gaio-HH44TBFINEIAvxtiuMwx3w@public.gmane.org>");
 MODULE_AUTHOR("Florian Fainelli <florian-p3rKhJxN3npAfugRpC6u6w@public.gmane.org>");
+MODULE_AUTHOR("Simon Arlott");
 MODULE_DESCRIPTION("Driver for the Broadcom BCM63xx SoC watchdog");
 MODULE_LICENSE("GPL");
 MODULE_ALIAS("platform:bcm63xx-wdt");
diff --git a/include/linux/bcm63xx_wdt.h b/include/linux/bcm63xx_wdt.h
new file mode 100644
index 0000000..ef4792e
--- /dev/null
+++ b/include/linux/bcm63xx_wdt.h
@@ -0,0 +1,22 @@
+#ifndef LINUX_BCM63XX_WDT_H_
+#define LINUX_BCM63XX_WDT_H_
+
+/* Watchdog default count register */
+#define WDT_DEFVAL_REG			0x0
+
+/* Watchdog control register */
+#define WDT_CTL_REG			0x4
+
+/* Watchdog control register constants */
+#define WDT_START_1			(0xff00)
+#define WDT_START_2			(0x00ff)
+#define WDT_STOP_1			(0xee00)
+#define WDT_STOP_2			(0x00ee)
+
+/* Watchdog reset length register (in clock ticks) */
+#define WDT_RSTLEN_REG			0x8
+
+/* Watchdog soft reset register (BCM6328 only) */
+#define WDT_SOFTRESET_REG		0xc
+
+#endif
-- 
2.1.4

-- 
Simon Arlott
--
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] 95+ messages in thread

* Re: [PATCH 4/4] MIPS: bmips: Convert bcm63xx_wdt to use WATCHDOG_CORE
  2015-11-21 19:05   ` Simon Arlott
  (?)
@ 2015-11-21 21:32   ` Guenter Roeck
  2015-11-21 21:44       ` Simon Arlott
  -1 siblings, 1 reply; 95+ messages in thread
From: Guenter Roeck @ 2015-11-21 21:32 UTC (permalink / raw)
  To: Simon Arlott, devicetree, Ralf Baechle, Thomas Gleixner,
	Jason Cooper, Marc Zyngier, Kevin Cernekee, Florian Fainelli,
	Wim Van Sebroeck, Miguel Gaio, Maxime Bizon,
	Linux Kernel Mailing List, linux-mips, linux-watchdog
  Cc: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala

On 11/21/2015 11:05 AM, Simon Arlott wrote:
> Convert bcm63xx_wdt to use WATCHDOG_CORE and add a device tree binding.
>
> Adds support for the time left value and provides a more effective
> interrupt handler based on the watchdog warning interrupt behaviour.
>
> This removes the unnecessary software countdown timer and replaces the
> use of bcm63xx_timer with a normal interrupt when not using mach-bcm63xx.
>

Hi Simon,

this is really doing a bit too much in a single patch.
Conversion to the watchdog infrastructure should probably be
the first step, followed by further optimizations and improvements.

In general, it would be great if we can avoid #ifdef in the code.
Maybe there is some other means to determine if one code path
needs to be taken or another. The driver may be part of a
multi-platform image, and #ifdefs in the code make that all
but impossible. Besides, it makes the code really hard to read
and understand.

We have some infrastructure changes in the works which will move
the need for soft-timers from individual drivers into the watchdog core.
Would this possibly be helpful here ? The timer-driven watchdog ping
seems to accomplish pretty much the same.

Thanks,
Guenter

> Signed-off-by: Simon Arlott <simon@fire.lp0.eu>
> ---
>   arch/mips/bcm63xx/prom.c                          |   1 +
>   arch/mips/bcm63xx/setup.c                         |   1 +
>   arch/mips/include/asm/mach-bcm63xx/bcm63xx_regs.h |  22 --
>   drivers/watchdog/Kconfig                          |   4 +-
>   drivers/watchdog/bcm63xx_wdt.c                    | 420 +++++++++++-----------
>   include/linux/bcm63xx_wdt.h                       |  22 ++
>   6 files changed, 244 insertions(+), 226 deletions(-)
>   create mode 100644 include/linux/bcm63xx_wdt.h
>
> diff --git a/arch/mips/bcm63xx/prom.c b/arch/mips/bcm63xx/prom.c
> index 7019e29..ba8b354 100644
> --- a/arch/mips/bcm63xx/prom.c
> +++ b/arch/mips/bcm63xx/prom.c
> @@ -17,6 +17,7 @@
>   #include <bcm63xx_cpu.h>
>   #include <bcm63xx_io.h>
>   #include <bcm63xx_regs.h>
> +#include <linux/bcm63xx_wdt.h>
>
>   void __init prom_init(void)
>   {
> diff --git a/arch/mips/bcm63xx/setup.c b/arch/mips/bcm63xx/setup.c
> index 240fb4f..6abf364 100644
> --- a/arch/mips/bcm63xx/setup.c
> +++ b/arch/mips/bcm63xx/setup.c
> @@ -21,6 +21,7 @@
>   #include <bcm63xx_regs.h>
>   #include <bcm63xx_io.h>
>   #include <bcm63xx_gpio.h>
> +#include <linux/bcm63xx_wdt.h>
>
>   void bcm63xx_machine_halt(void)
>   {
> diff --git a/arch/mips/include/asm/mach-bcm63xx/bcm63xx_regs.h b/arch/mips/include/asm/mach-bcm63xx/bcm63xx_regs.h
> index 5035f09..16a745b 100644
> --- a/arch/mips/include/asm/mach-bcm63xx/bcm63xx_regs.h
> +++ b/arch/mips/include/asm/mach-bcm63xx/bcm63xx_regs.h
> @@ -441,28 +441,6 @@
>
>
>   /*************************************************************************
> - * _REG relative to RSET_WDT
> - *************************************************************************/
> -
> -/* Watchdog default count register */
> -#define WDT_DEFVAL_REG			0x0
> -
> -/* Watchdog control register */
> -#define WDT_CTL_REG			0x4
> -
> -/* Watchdog control register constants */
> -#define WDT_START_1			(0xff00)
> -#define WDT_START_2			(0x00ff)
> -#define WDT_STOP_1			(0xee00)
> -#define WDT_STOP_2			(0x00ee)
> -
> -/* Watchdog reset length register */
> -#define WDT_RSTLEN_REG			0x8
> -
> -/* Watchdog soft reset register (BCM6328 only) */
> -#define WDT_SOFTRESET_REG		0xc
> -
> -/*************************************************************************
>    * _REG relative to RSET_GPIO
>    *************************************************************************/
>
> diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
> index 7a8a6c6..0c50add 100644
> --- a/drivers/watchdog/Kconfig
> +++ b/drivers/watchdog/Kconfig
> @@ -1272,7 +1272,9 @@ config OCTEON_WDT
>
>   config BCM63XX_WDT
>   	tristate "Broadcom BCM63xx hardware watchdog"
> -	depends on BCM63XX
> +	depends on BCM63XX || BMIPS_GENERIC
> +	select WATCHDOG_CORE
> +	select BCM6345_L2_TIMER_IRQ if BMIPS_GENERIC
>   	help
>   	  Watchdog driver for the built in watchdog hardware in Broadcom
>   	  BCM63xx SoC.
> diff --git a/drivers/watchdog/bcm63xx_wdt.c b/drivers/watchdog/bcm63xx_wdt.c
> index ab26fd9..fff92d0 100644
> --- a/drivers/watchdog/bcm63xx_wdt.c
> +++ b/drivers/watchdog/bcm63xx_wdt.c
> @@ -3,6 +3,7 @@
>    *
>    *  Copyright (C) 2007, Miguel Gaio <miguel.gaio@efixo.com>
>    *  Copyright (C) 2008, Florian Fainelli <florian@openwrt.org>
> + *  Copyright 2015 Simon Arlott
>    *
>    *  This program is free software; you can redistribute it and/or
>    *  modify it under the terms of the GNU General Public License
> @@ -12,235 +13,165 @@
>
>   #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
>
> -#include <linux/bitops.h>
> +#include <linux/bcm63xx_wdt.h>
> +#include <linux/clk.h>
> +#include <linux/delay.h>
>   #include <linux/errno.h>
> -#include <linux/fs.h>
> +#include <linux/interrupt.h>
>   #include <linux/io.h>
>   #include <linux/kernel.h>
> -#include <linux/miscdevice.h>
>   #include <linux/module.h>
>   #include <linux/moduleparam.h>
> +#include <linux/platform_device.h>
> +#include <linux/resource.h>
> +#include <linux/spinlock.h>
>   #include <linux/types.h>
> -#include <linux/uaccess.h>
>   #include <linux/watchdog.h>
> -#include <linux/timer.h>
> -#include <linux/jiffies.h>
> -#include <linux/interrupt.h>
> -#include <linux/ptrace.h>
> -#include <linux/resource.h>
> -#include <linux/platform_device.h>
>
> -#include <bcm63xx_cpu.h>
> -#include <bcm63xx_io.h>
> -#include <bcm63xx_regs.h>
> -#include <bcm63xx_timer.h>
> +#ifdef CONFIG_BCM63XX
> +# include <bcm63xx_regs.h>
> +# include <bcm63xx_timer.h>
> +#endif
>
>   #define PFX KBUILD_MODNAME
>
> -#define WDT_HZ		50000000 /* Fclk */
> -#define WDT_DEFAULT_TIME	30      /* seconds */
> -#define WDT_MAX_TIME		256     /* seconds */
> -
> -static struct {
> -	void __iomem *regs;
> -	struct timer_list timer;
> -	unsigned long inuse;
> -	atomic_t ticks;
> -} bcm63xx_wdt_device;
> -
> -static int expect_close;
> -
> -static int wdt_time = WDT_DEFAULT_TIME;
>   static bool nowayout = WATCHDOG_NOWAYOUT;
>   module_param(nowayout, bool, 0);
>   MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
>   	__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
>
> -/* HW functions */
> -static void bcm63xx_wdt_hw_start(void)
> -{
> -	bcm_writel(0xfffffffe, bcm63xx_wdt_device.regs + WDT_DEFVAL_REG);
> -	bcm_writel(WDT_START_1, bcm63xx_wdt_device.regs + WDT_CTL_REG);
> -	bcm_writel(WDT_START_2, bcm63xx_wdt_device.regs + WDT_CTL_REG);
> -}
> -
> -static void bcm63xx_wdt_hw_stop(void)
> -{
> -	bcm_writel(WDT_STOP_1, bcm63xx_wdt_device.regs + WDT_CTL_REG);
> -	bcm_writel(WDT_STOP_2, bcm63xx_wdt_device.regs + WDT_CTL_REG);
> -}
> -
> -static void bcm63xx_wdt_isr(void *data)
> -{
> -	struct pt_regs *regs = get_irq_regs();
> -
> -	die(PFX " fire", regs);
> -}
> -
> -static void bcm63xx_timer_tick(unsigned long unused)
> -{
> -	if (!atomic_dec_and_test(&bcm63xx_wdt_device.ticks)) {
> -		bcm63xx_wdt_hw_start();
> -		mod_timer(&bcm63xx_wdt_device.timer, jiffies + HZ);
> -	} else
> -		pr_crit("watchdog will restart system\n");
> -}
> -
> -static void bcm63xx_wdt_pet(void)
> -{
> -	atomic_set(&bcm63xx_wdt_device.ticks, wdt_time);
> -}
> -
> -static void bcm63xx_wdt_start(void)
> -{
> -	bcm63xx_wdt_pet();
> -	bcm63xx_timer_tick(0);
> -}
> +struct bcm63xx_wdt_hw {
> +	raw_spinlock_t lock;
> +	void __iomem *base;
> +	struct clk *clk;
> +	u32 clock_hz;
> +	int irq;
> +	bool running;
> +};
>
> -static void bcm63xx_wdt_pause(void)
> +static int bcm63xx_wdt_start(struct watchdog_device *wdd)
>   {
> -	del_timer_sync(&bcm63xx_wdt_device.timer);
> -	bcm63xx_wdt_hw_stop();
> +	struct bcm63xx_wdt_hw *hw = watchdog_get_drvdata(wdd);
> +	unsigned long flags;
> +
> +	raw_spin_lock_irqsave(&hw->lock, flags);
> +	__raw_writel(wdd->timeout * hw->clock_hz, hw->base + WDT_DEFVAL_REG);
> +	__raw_writel(WDT_START_1, hw->base + WDT_CTL_REG);
> +	__raw_writel(WDT_START_2, hw->base + WDT_CTL_REG);
> +	hw->running = true;
> +	raw_spin_unlock_irqrestore(&hw->lock, flags);
> +	return 0;
>   }
>
> -static int bcm63xx_wdt_settimeout(int new_time)
> +static int bcm63xx_wdt_stop(struct watchdog_device *wdd)
>   {
> -	if ((new_time <= 0) || (new_time > WDT_MAX_TIME))
> -		return -EINVAL;
> -
> -	wdt_time = new_time;
> -
> +	struct bcm63xx_wdt_hw *hw = watchdog_get_drvdata(wdd);
> +	unsigned long flags;
> +
> +	raw_spin_lock_irqsave(&hw->lock, flags);
> +	__raw_writel(WDT_STOP_1, hw->base + WDT_CTL_REG);
> +	__raw_writel(WDT_STOP_2, hw->base + WDT_CTL_REG);
> +	hw->running = false;
> +	raw_spin_unlock_irqrestore(&hw->lock, flags);
>   	return 0;
>   }
>
> -static int bcm63xx_wdt_open(struct inode *inode, struct file *file)
> +static unsigned int bcm63xx_wdt_get_timeleft(struct watchdog_device *wdd)
>   {
> -	if (test_and_set_bit(0, &bcm63xx_wdt_device.inuse))
> -		return -EBUSY;
> -
> -	bcm63xx_wdt_start();
> -	return nonseekable_open(inode, file);
> +	struct bcm63xx_wdt_hw *hw = watchdog_get_drvdata(wdd);
> +	unsigned long flags;
> +	u32 val;
> +
> +	raw_spin_lock_irqsave(&hw->lock, flags);
> +	val = __raw_readl(hw->base + WDT_CTL_REG);
> +	val /= hw->clock_hz;
> +	raw_spin_unlock_irqrestore(&hw->lock, flags);
> +	return val;
>   }
>
> -static int bcm63xx_wdt_release(struct inode *inode, struct file *file)
> +static int bcm63xx_wdt_set_timeout(struct watchdog_device *wdd,
> +	unsigned int timeout)
>   {
> -	if (expect_close == 42)
> -		bcm63xx_wdt_pause();
> -	else {
> -		pr_crit("Unexpected close, not stopping watchdog!\n");
> -		bcm63xx_wdt_start();
> -	}
> -	clear_bit(0, &bcm63xx_wdt_device.inuse);
> -	expect_close = 0;
> -	return 0;
> +	wdd->timeout = timeout;
> +	return bcm63xx_wdt_start(wdd);
>   }
>
> -static ssize_t bcm63xx_wdt_write(struct file *file, const char *data,
> -				size_t len, loff_t *ppos)
> +/* The watchdog interrupt occurs when half the timeout is remaining */
> +#ifdef CONFIG_BCM63XX
> +static void bcm63xx_wdt_interrupt(void *data)
> +#else
> +static irqreturn_t bcm63xx_wdt_interrupt(int irq, void *data)
> +#endif
>   {
> -	if (len) {
> -		if (!nowayout) {
> -			size_t i;
> -
> -			/* In case it was set long ago */
> -			expect_close = 0;
> -
> -			for (i = 0; i != len; i++) {
> -				char c;
> -				if (get_user(c, data + i))
> -					return -EFAULT;
> -				if (c == 'V')
> -					expect_close = 42;
> -			}
> +	struct watchdog_device *wdd = data;
> +	struct bcm63xx_wdt_hw *hw = watchdog_get_drvdata(wdd);
> +	unsigned long flags;
> +
> +	raw_spin_lock_irqsave(&hw->lock, flags);
> +	if (!hw->running) {
> +		/* Oops */
> +		__raw_writel(WDT_STOP_1, hw->base + WDT_CTL_REG);
> +		__raw_writel(WDT_STOP_2, hw->base + WDT_CTL_REG);
> +	} else {
> +		u32 timeleft = __raw_readl(hw->base + WDT_CTL_REG);
> +		u32 ms;
> +
> +		if (timeleft >= 2) {
> +			/* The only way to stop this interrupt without masking
> +			 * the whole timer interrupt or disrupting the intended
> +			 * behaviour of the watchdog is to restart the watchdog
> +			 * with the remaining time value so that the interrupt
> +			 * occurs again at 1/4th, 1/8th, etc. of the timeout
> +			 * until we reboot.
> +			 *
> +			 * This is done with a lock held in case userspace is
> +			 * restarting the watchdog on another CPU.
> +			 */
> +			__raw_writel(timeleft, hw->base + WDT_DEFVAL_REG);
> +			__raw_writel(WDT_START_1, hw->base + WDT_CTL_REG);
> +			__raw_writel(WDT_START_2, hw->base + WDT_CTL_REG);
> +		} else {
> +			/* The watchdog cannot be started with a time of less
> +			 * than 2 ticks (it won't fire).
> +			 */
> +			die(PFX ": watchdog timer expired\n", get_irq_regs());
>   		}
> -		bcm63xx_wdt_pet();
> -	}
> -	return len;
> -}
> -
> -static struct watchdog_info bcm63xx_wdt_info = {
> -	.identity       = PFX,
> -	.options        = WDIOF_SETTIMEOUT |
> -				WDIOF_KEEPALIVEPING |
> -				WDIOF_MAGICCLOSE,
> -};
> -
> -
> -static long bcm63xx_wdt_ioctl(struct file *file, unsigned int cmd,
> -				unsigned long arg)
> -{
> -	void __user *argp = (void __user *)arg;
> -	int __user *p = argp;
> -	int new_value, retval = -EINVAL;
> -
> -	switch (cmd) {
> -	case WDIOC_GETSUPPORT:
> -		return copy_to_user(argp, &bcm63xx_wdt_info,
> -			sizeof(bcm63xx_wdt_info)) ? -EFAULT : 0;
> -
> -	case WDIOC_GETSTATUS:
> -	case WDIOC_GETBOOTSTATUS:
> -		return put_user(0, p);
> -
> -	case WDIOC_SETOPTIONS:
> -		if (get_user(new_value, p))
> -			return -EFAULT;
> -
> -		if (new_value & WDIOS_DISABLECARD) {
> -			bcm63xx_wdt_pause();
> -			retval = 0;
> -		}
> -		if (new_value & WDIOS_ENABLECARD) {
> -			bcm63xx_wdt_start();
> -			retval = 0;
> -		}
> -
> -		return retval;
> -
> -	case WDIOC_KEEPALIVE:
> -		bcm63xx_wdt_pet();
> -		return 0;
> -
> -	case WDIOC_SETTIMEOUT:
> -		if (get_user(new_value, p))
> -			return -EFAULT;
> -
> -		if (bcm63xx_wdt_settimeout(new_value))
> -			return -EINVAL;
> -
> -		bcm63xx_wdt_pet();
> -
> -	case WDIOC_GETTIMEOUT:
> -		return put_user(wdt_time, p);
> -
> -	default:
> -		return -ENOTTY;
>
> +		ms = timeleft / (hw->clock_hz / 1000);
> +		dev_alert(wdd->dev, "warning timer fired, reboot in %ums", ms);
>   	}
> +	raw_spin_unlock_irqrestore(&hw->lock, flags);
> +#ifndef CONFIG_BCM63XX
> +	return IRQ_HANDLED;
> +#endif
>   }
>
> -static const struct file_operations bcm63xx_wdt_fops = {
> -	.owner		= THIS_MODULE,
> -	.llseek		= no_llseek,
> -	.write		= bcm63xx_wdt_write,
> -	.unlocked_ioctl	= bcm63xx_wdt_ioctl,
> -	.open		= bcm63xx_wdt_open,
> -	.release	= bcm63xx_wdt_release,
> +static struct watchdog_ops bcm63xx_wdt_ops = {
> +	.owner = THIS_MODULE,
> +	.start = bcm63xx_wdt_start,
> +	.stop = bcm63xx_wdt_stop,
> +	.get_timeleft = bcm63xx_wdt_get_timeleft,
> +	.set_timeout = bcm63xx_wdt_set_timeout,
>   };
>
> -static struct miscdevice bcm63xx_wdt_miscdev = {
> -	.minor	= WATCHDOG_MINOR,
> -	.name	= "watchdog",
> -	.fops	= &bcm63xx_wdt_fops,
> +static const struct watchdog_info bcm63xx_wdt_info = {
> +	.options = WDIOC_GETTIMELEFT | WDIOF_SETTIMEOUT |
> +			WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
> +	.identity = "BCM63xx Watchdog",
>   };
>
> -
>   static int bcm63xx_wdt_probe(struct platform_device *pdev)
>   {
> -	int ret;
> +	struct bcm63xx_wdt_hw *hw;
> +	struct watchdog_device *wdd;
>   	struct resource *r;
> +	unsigned int timeleft;
> +	int ret;
>
> -	setup_timer(&bcm63xx_wdt_device.timer, bcm63xx_timer_tick, 0L);
> +	hw = devm_kzalloc(&pdev->dev, sizeof(*hw), GFP_KERNEL);
> +	wdd = devm_kzalloc(&pdev->dev, sizeof(*wdd), GFP_KERNEL);
> +	if (!hw || !wdd)
> +		return -ENOMEM;
>
>   	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>   	if (!r) {
> @@ -248,63 +179,145 @@ static int bcm63xx_wdt_probe(struct platform_device *pdev)
>   		return -ENODEV;
>   	}
>
> -	bcm63xx_wdt_device.regs = devm_ioremap_nocache(&pdev->dev, r->start,
> -							resource_size(r));
> -	if (!bcm63xx_wdt_device.regs) {
> +	hw->base = devm_ioremap_nocache(&pdev->dev, r->start, resource_size(r));
> +	if (!hw->base) {
>   		dev_err(&pdev->dev, "failed to remap I/O resources\n");
>   		return -ENXIO;
>   	}
>
> -	ret = bcm63xx_timer_register(TIMER_WDT_ID, bcm63xx_wdt_isr, NULL);
> -	if (ret < 0) {
> -		dev_err(&pdev->dev, "failed to register wdt timer isr\n");
> +#ifdef CONFIG_BCM63XX
> +	hw->clk = devm_clk_get(&pdev->dev, "periph");
> +#else
> +	hw->clk = devm_clk_get(&pdev->dev, NULL);
> +#endif
> +	if (IS_ERR(hw->clk)) {
> +		dev_err(&pdev->dev, "unable to request clock\n");
> +		return PTR_ERR(hw->clk);
> +	}
> +
> +	hw->clock_hz = clk_get_rate(hw->clk);
> +	if (!hw->clock_hz) {
> +		dev_err(&pdev->dev, "unable to fetch clock rate\n");
> +		return -EINVAL;
> +	}
> +
> +	ret = clk_prepare_enable(hw->clk);
> +	if (ret) {
> +		dev_err(&pdev->dev, "unable to enable clock\n");
>   		return ret;
>   	}
>
> -	if (bcm63xx_wdt_settimeout(wdt_time)) {
> -		bcm63xx_wdt_settimeout(WDT_DEFAULT_TIME);
> -		dev_info(&pdev->dev,
> -			": wdt_time value must be 1 <= wdt_time <= 256, using %d\n",
> -			wdt_time);
> +	raw_spin_lock_init(&hw->lock);
> +	hw->running = false;
> +
> +	wdd->parent = &pdev->dev;
> +	wdd->ops = &bcm63xx_wdt_ops;
> +	wdd->info = &bcm63xx_wdt_info;
> +	wdd->min_timeout = 1;
> +	wdd->max_timeout = 0xffffffff / hw->clock_hz;
> +	wdd->timeout = min(30U, wdd->max_timeout);
> +
> +	watchdog_set_drvdata(wdd, hw);
> +	platform_set_drvdata(pdev, wdd);
> +
> +	watchdog_init_timeout(wdd, 0, &pdev->dev);
> +	watchdog_set_nowayout(wdd, nowayout);
> +
> +	timeleft = bcm63xx_wdt_get_timeleft(wdd);
> +	if (timeleft > 0)
> +		hw->running = true;
> +
> +#ifdef CONFIG_BCM63XX
> +	ret = bcm63xx_timer_register(TIMER_WDT_ID, bcm63xx_wdt_interrupt, wdd);
> +	if (ret) {
> +		dev_err(&pdev->dev, "failed to register with bcm63xx_timer\n");
> +		goto disable_clk;
>   	}
> +	hw->irq = 0;
> +#endif
>
> -	ret = misc_register(&bcm63xx_wdt_miscdev);
> +	ret = watchdog_register_device(wdd);
>   	if (ret < 0) {
>   		dev_err(&pdev->dev, "failed to register watchdog device\n");
> +#ifdef CONFIG_BCM63XX
>   		goto unregister_timer;
> +#else
> +		goto disable_clk;
> +#endif
>   	}
>
> -	dev_info(&pdev->dev, " started, timer margin: %d sec\n",
> -						WDT_DEFAULT_TIME);
> +#ifndef CONFIG_BCM63XX
> +	hw->irq = platform_get_irq(pdev, 0);
> +	if (hw->irq) {
> +		ret = devm_request_irq(&pdev->dev, hw->irq,
> +			bcm63xx_wdt_interrupt, IRQF_TIMER,
> +			dev_name(&pdev->dev), wdd);
> +		if (ret)
> +			hw->irq = 0;
> +	}
> +#endif
> +
> +	if (hw->irq) {
> +		dev_info(&pdev->dev,
> +			"%s at MMIO 0x%p (irq = %d, timeout = %us, max_timeout = %us)",
> +			dev_name(wdd->dev), hw->base, hw->irq,
> +			wdd->timeout, wdd->max_timeout);
> +	} else {
> +		dev_info(&pdev->dev,
> +			"%s at MMIO 0x%p (timeout = %us, max_timeout = %us)",
> +			dev_name(wdd->dev), hw->base,
> +			wdd->timeout, wdd->max_timeout);
> +	}
>
> +	if (timeleft > 0)
> +		dev_alert(wdd->dev, "running, reboot in %us\n", timeleft);
>   	return 0;
>
> +#ifdef CONFIG_BCM63XX
>   unregister_timer:
>   	bcm63xx_timer_unregister(TIMER_WDT_ID);
> +#endif
> +disable_clk:
> +	clk_disable(hw->clk);
>   	return ret;
>   }
>
>   static int bcm63xx_wdt_remove(struct platform_device *pdev)
>   {
> -	if (!nowayout)
> -		bcm63xx_wdt_pause();
> +	struct watchdog_device *wdd = platform_get_drvdata(pdev);
> +	struct bcm63xx_wdt_hw *hw = watchdog_get_drvdata(wdd);
>
> -	misc_deregister(&bcm63xx_wdt_miscdev);
> +	if (hw->irq)
> +		devm_free_irq(&pdev->dev, hw->irq, wdd);
> +
> +#ifdef CONFIG_BCM63XX
>   	bcm63xx_timer_unregister(TIMER_WDT_ID);
> +#endif
> +	watchdog_unregister_device(wdd);
> +	clk_disable(hw->clk);
>   	return 0;
>   }
>
>   static void bcm63xx_wdt_shutdown(struct platform_device *pdev)
>   {
> -	bcm63xx_wdt_pause();
> +	struct watchdog_device *wdd = platform_get_drvdata(pdev);
> +
> +	bcm63xx_wdt_stop(wdd);
>   }
>
> +static const struct of_device_id bcm63xx_wdt_dt_ids[] = {
> +	{ .compatible = "brcm,bcm6345-wdt" },
> +	{}
> +};
> +MODULE_DEVICE_TABLE(of, bcm63xx_wdt_dt_ids);
> +
>   static struct platform_driver bcm63xx_wdt_driver = {
>   	.probe	= bcm63xx_wdt_probe,
>   	.remove = bcm63xx_wdt_remove,
>   	.shutdown = bcm63xx_wdt_shutdown,
>   	.driver = {
>   		.name = "bcm63xx-wdt",
> +		.of_match_table = bcm63xx_wdt_dt_ids,
>   	}
>   };
>
> @@ -312,6 +325,7 @@ module_platform_driver(bcm63xx_wdt_driver);
>
>   MODULE_AUTHOR("Miguel Gaio <miguel.gaio@efixo.com>");
>   MODULE_AUTHOR("Florian Fainelli <florian@openwrt.org>");
> +MODULE_AUTHOR("Simon Arlott");
>   MODULE_DESCRIPTION("Driver for the Broadcom BCM63xx SoC watchdog");
>   MODULE_LICENSE("GPL");
>   MODULE_ALIAS("platform:bcm63xx-wdt");
> diff --git a/include/linux/bcm63xx_wdt.h b/include/linux/bcm63xx_wdt.h
> new file mode 100644
> index 0000000..ef4792e
> --- /dev/null
> +++ b/include/linux/bcm63xx_wdt.h
> @@ -0,0 +1,22 @@
> +#ifndef LINUX_BCM63XX_WDT_H_
> +#define LINUX_BCM63XX_WDT_H_
> +
> +/* Watchdog default count register */
> +#define WDT_DEFVAL_REG			0x0
> +
> +/* Watchdog control register */
> +#define WDT_CTL_REG			0x4
> +
> +/* Watchdog control register constants */
> +#define WDT_START_1			(0xff00)
> +#define WDT_START_2			(0x00ff)
> +#define WDT_STOP_1			(0xee00)
> +#define WDT_STOP_2			(0x00ee)
> +
> +/* Watchdog reset length register (in clock ticks) */
> +#define WDT_RSTLEN_REG			0x8
> +
> +/* Watchdog soft reset register (BCM6328 only) */
> +#define WDT_SOFTRESET_REG		0xc
> +
> +#endif
>


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

* Re: [PATCH 4/4] MIPS: bmips: Convert bcm63xx_wdt to use WATCHDOG_CORE
@ 2015-11-21 21:44       ` Simon Arlott
  0 siblings, 0 replies; 95+ messages in thread
From: Simon Arlott @ 2015-11-21 21:44 UTC (permalink / raw)
  To: Guenter Roeck, devicetree, Ralf Baechle, Thomas Gleixner,
	Jason Cooper, Marc Zyngier, Kevin Cernekee, Florian Fainelli,
	Wim Van Sebroeck, Miguel Gaio, Maxime Bizon,
	Linux Kernel Mailing List, linux-mips, linux-watchdog
  Cc: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala

On 21/11/15 21:32, Guenter Roeck wrote:
> On 11/21/2015 11:05 AM, Simon Arlott wrote:
>> Convert bcm63xx_wdt to use WATCHDOG_CORE and add a device tree binding.
>>
>> Adds support for the time left value and provides a more effective
>> interrupt handler based on the watchdog warning interrupt behaviour.
>>
>> This removes the unnecessary software countdown timer and replaces the
>> use of bcm63xx_timer with a normal interrupt when not using mach-bcm63xx.
>>
> 
> Hi Simon,
> 
> this is really doing a bit too much in a single patch.
> Conversion to the watchdog infrastructure should probably be
> the first step, followed by further optimizations and improvements.

I'll split it into two patches, but that won't remove the need for #ifdefs.

> In general, it would be great if we can avoid #ifdef in the code.
> Maybe there is some other means to determine if one code path
> needs to be taken or another. The driver may be part of a
> multi-platform image, and #ifdefs in the code make that all
> but impossible. Besides, it makes the code really hard to read
> and understand.

It's impossible to avoid the #ifdefs because the driver needs to support
mach-bmips while still supporting mach-bcm63xx. I don't think they make
it too difficult to understand. Until there are device tree supporting
drivers for everything mach-bcm63xx needs, it can't be removed.

> We have some infrastructure changes in the works which will move
> the need for soft-timers from individual drivers into the watchdog core.
> Would this possibly be helpful here ? The timer-driven watchdog ping
> seems to accomplish pretty much the same.

There is no need for a software timer. This is not a timer-driven
watchdog ping, there is an unmaskable timer interrupt when the watchdog
timer has less than 50% remaining.

-- 
Simon Arlott

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

* Re: [PATCH 4/4] MIPS: bmips: Convert bcm63xx_wdt to use WATCHDOG_CORE
@ 2015-11-21 21:44       ` Simon Arlott
  0 siblings, 0 replies; 95+ messages in thread
From: Simon Arlott @ 2015-11-21 21:44 UTC (permalink / raw)
  To: Guenter Roeck, devicetree-u79uwXL29TY76Z2rM5mHXA, Ralf Baechle,
	Thomas Gleixner, Jason Cooper, Marc Zyngier, Kevin Cernekee,
	Florian Fainelli, Wim Van Sebroeck, Miguel Gaio, Maxime Bizon,
	Linux Kernel Mailing List, linux-mips-6z/3iImG2C8G8FEW9MqTrA,
	linux-watchdog-u79uwXL29TY76Z2rM5mHXA
  Cc: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala

On 21/11/15 21:32, Guenter Roeck wrote:
> On 11/21/2015 11:05 AM, Simon Arlott wrote:
>> Convert bcm63xx_wdt to use WATCHDOG_CORE and add a device tree binding.
>>
>> Adds support for the time left value and provides a more effective
>> interrupt handler based on the watchdog warning interrupt behaviour.
>>
>> This removes the unnecessary software countdown timer and replaces the
>> use of bcm63xx_timer with a normal interrupt when not using mach-bcm63xx.
>>
> 
> Hi Simon,
> 
> this is really doing a bit too much in a single patch.
> Conversion to the watchdog infrastructure should probably be
> the first step, followed by further optimizations and improvements.

I'll split it into two patches, but that won't remove the need for #ifdefs.

> In general, it would be great if we can avoid #ifdef in the code.
> Maybe there is some other means to determine if one code path
> needs to be taken or another. The driver may be part of a
> multi-platform image, and #ifdefs in the code make that all
> but impossible. Besides, it makes the code really hard to read
> and understand.

It's impossible to avoid the #ifdefs because the driver needs to support
mach-bmips while still supporting mach-bcm63xx. I don't think they make
it too difficult to understand. Until there are device tree supporting
drivers for everything mach-bcm63xx needs, it can't be removed.

> We have some infrastructure changes in the works which will move
> the need for soft-timers from individual drivers into the watchdog core.
> Would this possibly be helpful here ? The timer-driven watchdog ping
> seems to accomplish pretty much the same.

There is no need for a software timer. This is not a timer-driven
watchdog ping, there is an unmaskable timer interrupt when the watchdog
timer has less than 50% remaining.

-- 
Simon Arlott
--
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] 95+ messages in thread

* Re: [PATCH 4/4] MIPS: bmips: Convert bcm63xx_wdt to use WATCHDOG_CORE
  2015-11-21 21:44       ` Simon Arlott
  (?)
@ 2015-11-22  2:32       ` Guenter Roeck
  2015-11-22 14:02         ` [PATCH 4/10] (Was: [PATCH 4/4]) " Simon Arlott
  -1 siblings, 1 reply; 95+ messages in thread
From: Guenter Roeck @ 2015-11-22  2:32 UTC (permalink / raw)
  To: Simon Arlott, devicetree, Ralf Baechle, Thomas Gleixner,
	Jason Cooper, Marc Zyngier, Kevin Cernekee, Florian Fainelli,
	Wim Van Sebroeck, Miguel Gaio, Maxime Bizon,
	Linux Kernel Mailing List, linux-mips, linux-watchdog
  Cc: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala

On 11/21/2015 01:44 PM, Simon Arlott wrote:
> On 21/11/15 21:32, Guenter Roeck wrote:
>> On 11/21/2015 11:05 AM, Simon Arlott wrote:
>>> Convert bcm63xx_wdt to use WATCHDOG_CORE and add a device tree binding.
>>>
>>> Adds support for the time left value and provides a more effective
>>> interrupt handler based on the watchdog warning interrupt behaviour.
>>>
>>> This removes the unnecessary software countdown timer and replaces the
>>> use of bcm63xx_timer with a normal interrupt when not using mach-bcm63xx.
>>>
>>
>> Hi Simon,
>>
>> this is really doing a bit too much in a single patch.
>> Conversion to the watchdog infrastructure should probably be
>> the first step, followed by further optimizations and improvements.
>
> I'll split it into two patches, but that won't remove the need for #ifdefs.
>
>> In general, it would be great if we can avoid #ifdef in the code.
>> Maybe there is some other means to determine if one code path
>> needs to be taken or another. The driver may be part of a
>> multi-platform image, and #ifdefs in the code make that all
>> but impossible. Besides, it makes the code really hard to read
>> and understand.
>
> It's impossible to avoid the #ifdefs because the driver needs to support
> mach-bmips while still supporting mach-bcm63xx. I don't think they make
> it too difficult to understand. Until there are device tree supporting
> drivers for everything mach-bcm63xx needs, it can't be removed.
>

Even if ifdefs are needed, they don't need to be as extensive as they are.
#ifdef around function names can be handled with shim functions, different
clock names can be handled by defining the clock name per platform.
The interrupt handler registration may not require an #ifdef if it is
just made optional. Conditional include files are typically not needed
at all.

>> We have some infrastructure changes in the works which will move
>> the need for soft-timers from individual drivers into the watchdog core.
>> Would this possibly be helpful here ? The timer-driven watchdog ping
>> seems to accomplish pretty much the same.
>
> There is no need for a software timer. This is not a timer-driven
> watchdog ping, there is an unmaskable timer interrupt when the watchdog
> timer has less than 50% remaining.
>
Ok. Maybe I got confused by the interrupt-triggered watchdog ping.
I'll have to look into that much more closely; it is quite unusual
and complex. The explanation is also not easy to understand.
What does "The only way to stop this interrupt" mean ? Repeatedly
triggering the interrupt in 1/2, 1/4, 1/8 of the remaining time is
really odd.

On side note, the subject tag should be "watchdog:", not "MIPS:".

Thanks,
Guenter


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

* Re: [PATCH 4/10] (Was: [PATCH 4/4]) MIPS: bmips: Convert bcm63xx_wdt to use WATCHDOG_CORE
  2015-11-22  2:32       ` Guenter Roeck
@ 2015-11-22 14:02         ` Simon Arlott
  2015-11-22 14:05           ` [PATCH 4/10] watchdog: bcm63xx_wdt: Handle hardware interrupt and remove software timer Simon Arlott
                             ` (6 more replies)
  0 siblings, 7 replies; 95+ messages in thread
From: Simon Arlott @ 2015-11-22 14:02 UTC (permalink / raw)
  To: Guenter Roeck, devicetree, Ralf Baechle, Thomas Gleixner,
	Jason Cooper, Marc Zyngier, Kevin Cernekee, Florian Fainelli,
	Wim Van Sebroeck, Miguel Gaio, Maxime Bizon,
	Linux Kernel Mailing List, linux-mips, linux-watchdog
  Cc: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
	Jonas Gorski

On 22/11/15 02:32, Guenter Roeck wrote:
> On 11/21/2015 01:44 PM, Simon Arlott wrote:
>> On 21/11/15 21:32, Guenter Roeck wrote:
>>> this is really doing a bit too much in a single patch.
>>> Conversion to the watchdog infrastructure should probably be
>>> the first step, followed by further optimizations and improvements.
>>

I've split patch 4 up into 7 patches, which will be patches 4/10..10/10
in this thread.

Instead of using bcm63xx_timer_register in #ifdefs, I'll remove most of
bcm63xx_timer because it's only used by the watchdog.

>>> We have some infrastructure changes in the works which will move
>>> the need for soft-timers from individual drivers into the watchdog core.
>>> Would this possibly be helpful here ? The timer-driven watchdog ping
>>> seems to accomplish pretty much the same.
>>
>> There is no need for a software timer. This is not a timer-driven
>> watchdog ping, there is an unmaskable timer interrupt when the watchdog
>> timer has less than 50% remaining.
>>
> Ok. Maybe I got confused by the interrupt-triggered watchdog ping.
> I'll have to look into that much more closely; it is quite unusual
> and complex. The explanation is also not easy to understand.
> What does "The only way to stop this interrupt" mean ? Repeatedly

The interrupt is level triggered at less than 50% of the time remaining.
Unless the watchdog is stopped or restarted, the interrupt will not stop
occurring.

> triggering the interrupt in 1/2, 1/4, 1/8 of the remaining time is
> really odd.

It's a bit odd but there's no way to ack the interrupt. This seemed like
the best approach without adding the complexity of a software timer or
trying to mask and unmask the timer interrupt. I don't  want to ignore
the interrupt entirely because I'd like to know if the watchdog is going
to cause a reboot.

> On side note, the subject tag should be "watchdog:", not "MIPS:".

Fixed.

-- 
Simon Arlott

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

* [PATCH 4/10] watchdog: bcm63xx_wdt: Handle hardware interrupt and remove software timer
  2015-11-22 14:02         ` [PATCH 4/10] (Was: [PATCH 4/4]) " Simon Arlott
@ 2015-11-22 14:05           ` Simon Arlott
  2015-11-24 18:21               ` Guenter Roeck
  2015-11-25 22:33               ` Simon Arlott
  2015-11-22 14:06             ` Simon Arlott
                             ` (5 subsequent siblings)
  6 siblings, 2 replies; 95+ messages in thread
From: Simon Arlott @ 2015-11-22 14:05 UTC (permalink / raw)
  To: Guenter Roeck, devicetree, Ralf Baechle, Thomas Gleixner,
	Jason Cooper, Marc Zyngier, Kevin Cernekee, Florian Fainelli,
	Wim Van Sebroeck, Maxime Bizon, Linux Kernel Mailing List,
	linux-mips, linux-watchdog
  Cc: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
	Jonas Gorski

There is a level triggered interrupt for the watchdog timer as part of
the bcm63xx_timer device. The interrupt occurs when the hardware watchdog
timer reaches 50% of the remaining time.

It is not possible to mask the interrupt within the bcm63xx_timer device.
To get around this limitation, handle the interrupt by restarting the
watchdog with the current remaining time (which will be half the previous
timeout) so that the interrupt occurs again at 1/4th, 1/8th, etc. of the
original timeout value until the watchdog forces a reboot.

The software timer was restarting the hardware watchdog with a 85 second
timeout until the software timer expired, and then causing a panic()
about 42.5 seconds later when the hardware interrupt occurred. The
hardware watchdog would not reboot until a further 42.5 seconds had
passed.

Remove the software timer and rely on the hardware timer directly,
reducing the maximum timeout from 256 seconds to 85 seconds
(2^32 / WDT_HZ).

Signed-off-by: Simon Arlott <simon@fire.lp0.eu>
---
 drivers/watchdog/bcm63xx_wdt.c | 124 ++++++++++++++++++++++++-----------------
 1 file changed, 72 insertions(+), 52 deletions(-)

diff --git a/drivers/watchdog/bcm63xx_wdt.c b/drivers/watchdog/bcm63xx_wdt.c
index ab26fd9..f88fc97 100644
--- a/drivers/watchdog/bcm63xx_wdt.c
+++ b/drivers/watchdog/bcm63xx_wdt.c
@@ -3,6 +3,7 @@
  *
  *  Copyright (C) 2007, Miguel Gaio <miguel.gaio@efixo.com>
  *  Copyright (C) 2008, Florian Fainelli <florian@openwrt.org>
+ *  Copyright 2015 Simon Arlott
  *
  *  This program is free software; you can redistribute it and/or
  *  modify it under the terms of the GNU General Public License
@@ -20,11 +21,10 @@
 #include <linux/miscdevice.h>
 #include <linux/module.h>
 #include <linux/moduleparam.h>
+#include <linux/spinlock.h>
 #include <linux/types.h>
 #include <linux/uaccess.h>
 #include <linux/watchdog.h>
-#include <linux/timer.h>
-#include <linux/jiffies.h>
 #include <linux/interrupt.h>
 #include <linux/ptrace.h>
 #include <linux/resource.h>
@@ -37,16 +37,17 @@
 
 #define PFX KBUILD_MODNAME
 
-#define WDT_HZ		50000000 /* Fclk */
-#define WDT_DEFAULT_TIME	30      /* seconds */
-#define WDT_MAX_TIME		256     /* seconds */
+#define WDT_HZ			50000000		/* Fclk */
+#define WDT_DEFAULT_TIME	30			/* seconds */
+#define WDT_MAX_TIME		(0xffffffff / WDT_HZ)	/* seconds */
 
-static struct {
+struct bcm63xx_wdt_hw {
+	raw_spinlock_t lock;
 	void __iomem *regs;
-	struct timer_list timer;
 	unsigned long inuse;
-	atomic_t ticks;
-} bcm63xx_wdt_device;
+	bool running;
+};
+static struct bcm63xx_wdt_hw bcm63xx_wdt_device;
 
 static int expect_close;
 
@@ -59,48 +60,67 @@ MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
 /* HW functions */
 static void bcm63xx_wdt_hw_start(void)
 {
-	bcm_writel(0xfffffffe, bcm63xx_wdt_device.regs + WDT_DEFVAL_REG);
+	unsigned long flags;
+
+	raw_spin_lock_irqsave(&bcm63xx_wdt_device.lock, flags);
+	bcm_writel(wdt_time * WDT_HZ, bcm63xx_wdt_device.regs + WDT_DEFVAL_REG);
 	bcm_writel(WDT_START_1, bcm63xx_wdt_device.regs + WDT_CTL_REG);
 	bcm_writel(WDT_START_2, bcm63xx_wdt_device.regs + WDT_CTL_REG);
+	bcm63xx_wdt_device.running = true;
+	raw_spin_unlock_irqrestore(&bcm63xx_wdt_device.lock, flags);
 }
 
 static void bcm63xx_wdt_hw_stop(void)
 {
+	unsigned long flags;
+
+	raw_spin_lock_irqsave(&bcm63xx_wdt_device.lock, flags);
 	bcm_writel(WDT_STOP_1, bcm63xx_wdt_device.regs + WDT_CTL_REG);
 	bcm_writel(WDT_STOP_2, bcm63xx_wdt_device.regs + WDT_CTL_REG);
+	bcm63xx_wdt_device.running = false;
+	raw_spin_unlock_irqrestore(&bcm63xx_wdt_device.lock, flags);
 }
 
+/* The watchdog interrupt occurs when half the timeout is remaining */
 static void bcm63xx_wdt_isr(void *data)
 {
-	struct pt_regs *regs = get_irq_regs();
-
-	die(PFX " fire", regs);
-}
-
-static void bcm63xx_timer_tick(unsigned long unused)
-{
-	if (!atomic_dec_and_test(&bcm63xx_wdt_device.ticks)) {
-		bcm63xx_wdt_hw_start();
-		mod_timer(&bcm63xx_wdt_device.timer, jiffies + HZ);
-	} else
-		pr_crit("watchdog will restart system\n");
-}
-
-static void bcm63xx_wdt_pet(void)
-{
-	atomic_set(&bcm63xx_wdt_device.ticks, wdt_time);
-}
-
-static void bcm63xx_wdt_start(void)
-{
-	bcm63xx_wdt_pet();
-	bcm63xx_timer_tick(0);
-}
+	struct bcm63xx_wdt_hw *hw = &bcm63xx_wdt_device;
+	unsigned long flags;
+
+	raw_spin_lock_irqsave(&hw->lock, flags);
+	if (!hw->running) {
+		/* Stop the watchdog as it shouldn't be running */
+		bcm_writel(WDT_STOP_1, hw->regs + WDT_CTL_REG);
+		bcm_writel(WDT_STOP_2, hw->regs + WDT_CTL_REG);
+	} else {
+		u32 timeleft = bcm_readl(hw->regs + WDT_CTL_REG);
+		u32 ms;
+
+		if (timeleft >= 2) {
+			/* The only way to clear this level triggered interrupt
+			 * without disrupting the normal running of the watchdog
+			 * is to restart the watchdog with the current remaining
+			 * time value (which will be half the previous timeout)
+			 * so the interrupt occurs again at 1/4th, 1/8th, etc.
+			 * of the original timeout value until we reboot.
+			 *
+			 * This is done with a lock held in case userspace is
+			 * trying to restart the watchdog on another CPU.
+			 */
+			bcm_writel(timeleft, hw->regs + WDT_DEFVAL_REG);
+			bcm_writel(WDT_START_1, hw->regs + WDT_CTL_REG);
+			bcm_writel(WDT_START_2, hw->regs + WDT_CTL_REG);
+		} else {
+			/* The watchdog cannot be started with a time of less
+			 * than 2 ticks (it won't fire).
+			 */
+			die(PFX ": watchdog timer expired\n", get_irq_regs());
+		}
 
-static void bcm63xx_wdt_pause(void)
-{
-	del_timer_sync(&bcm63xx_wdt_device.timer);
-	bcm63xx_wdt_hw_stop();
+		ms = timeleft / (WDT_HZ / 1000);
+		pr_alert("warning timer fired, reboot in %ums\n", ms);
+	}
+	raw_spin_unlock_irqrestore(&hw->lock, flags);
 }
 
 static int bcm63xx_wdt_settimeout(int new_time)
@@ -118,17 +138,17 @@ static int bcm63xx_wdt_open(struct inode *inode, struct file *file)
 	if (test_and_set_bit(0, &bcm63xx_wdt_device.inuse))
 		return -EBUSY;
 
-	bcm63xx_wdt_start();
+	bcm63xx_wdt_hw_start();
 	return nonseekable_open(inode, file);
 }
 
 static int bcm63xx_wdt_release(struct inode *inode, struct file *file)
 {
 	if (expect_close == 42)
-		bcm63xx_wdt_pause();
+		bcm63xx_wdt_hw_stop();
 	else {
 		pr_crit("Unexpected close, not stopping watchdog!\n");
-		bcm63xx_wdt_start();
+		bcm63xx_wdt_hw_start();
 	}
 	clear_bit(0, &bcm63xx_wdt_device.inuse);
 	expect_close = 0;
@@ -153,7 +173,7 @@ static ssize_t bcm63xx_wdt_write(struct file *file, const char *data,
 					expect_close = 42;
 			}
 		}
-		bcm63xx_wdt_pet();
+		bcm63xx_wdt_hw_start();
 	}
 	return len;
 }
@@ -187,18 +207,18 @@ static long bcm63xx_wdt_ioctl(struct file *file, unsigned int cmd,
 			return -EFAULT;
 
 		if (new_value & WDIOS_DISABLECARD) {
-			bcm63xx_wdt_pause();
+			bcm63xx_wdt_hw_stop();
 			retval = 0;
 		}
 		if (new_value & WDIOS_ENABLECARD) {
-			bcm63xx_wdt_start();
+			bcm63xx_wdt_hw_start();
 			retval = 0;
 		}
 
 		return retval;
 
 	case WDIOC_KEEPALIVE:
-		bcm63xx_wdt_pet();
+		bcm63xx_wdt_hw_start();
 		return 0;
 
 	case WDIOC_SETTIMEOUT:
@@ -208,7 +228,7 @@ static long bcm63xx_wdt_ioctl(struct file *file, unsigned int cmd,
 		if (bcm63xx_wdt_settimeout(new_value))
 			return -EINVAL;
 
-		bcm63xx_wdt_pet();
+		bcm63xx_wdt_hw_start();
 
 	case WDIOC_GETTIMEOUT:
 		return put_user(wdt_time, p);
@@ -240,8 +260,6 @@ static int bcm63xx_wdt_probe(struct platform_device *pdev)
 	int ret;
 	struct resource *r;
 
-	setup_timer(&bcm63xx_wdt_device.timer, bcm63xx_timer_tick, 0L);
-
 	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 	if (!r) {
 		dev_err(&pdev->dev, "failed to get resources\n");
@@ -255,6 +273,8 @@ static int bcm63xx_wdt_probe(struct platform_device *pdev)
 		return -ENXIO;
 	}
 
+	raw_spin_lock_init(&bcm63xx_wdt_device.lock);
+
 	ret = bcm63xx_timer_register(TIMER_WDT_ID, bcm63xx_wdt_isr, NULL);
 	if (ret < 0) {
 		dev_err(&pdev->dev, "failed to register wdt timer isr\n");
@@ -264,8 +284,8 @@ static int bcm63xx_wdt_probe(struct platform_device *pdev)
 	if (bcm63xx_wdt_settimeout(wdt_time)) {
 		bcm63xx_wdt_settimeout(WDT_DEFAULT_TIME);
 		dev_info(&pdev->dev,
-			": wdt_time value must be 1 <= wdt_time <= 256, using %d\n",
-			wdt_time);
+			": wdt_time value must be 1 <= wdt_time <= %d, using %d\n",
+			WDT_MAX_TIME, wdt_time);
 	}
 
 	ret = misc_register(&bcm63xx_wdt_miscdev);
@@ -287,7 +307,7 @@ unregister_timer:
 static int bcm63xx_wdt_remove(struct platform_device *pdev)
 {
 	if (!nowayout)
-		bcm63xx_wdt_pause();
+		bcm63xx_wdt_hw_stop();
 
 	misc_deregister(&bcm63xx_wdt_miscdev);
 	bcm63xx_timer_unregister(TIMER_WDT_ID);
@@ -296,7 +316,7 @@ static int bcm63xx_wdt_remove(struct platform_device *pdev)
 
 static void bcm63xx_wdt_shutdown(struct platform_device *pdev)
 {
-	bcm63xx_wdt_pause();
+	bcm63xx_wdt_hw_stop();
 }
 
 static struct platform_driver bcm63xx_wdt_driver = {
-- 
2.1.4

-- 
Simon Arlott

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

* [PATCH 5/10] watchdog: bcm63xx_wdt: Use WATCHDOG_CORE
@ 2015-11-22 14:06             ` Simon Arlott
  0 siblings, 0 replies; 95+ messages in thread
From: Simon Arlott @ 2015-11-22 14:06 UTC (permalink / raw)
  To: Guenter Roeck, devicetree, Ralf Baechle, Thomas Gleixner,
	Jason Cooper, Marc Zyngier, Kevin Cernekee, Florian Fainelli,
	Wim Van Sebroeck, Maxime Bizon, Linux Kernel Mailing List,
	linux-mips, linux-watchdog
  Cc: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
	Jonas Gorski

Convert bcm63xx_wdt to use WATCHDOG_CORE.

The default and maximum time constants that are only used once have been
moved to the initialisation of the struct watchdog_device.

Signed-off-by: Simon Arlott <simon@fire.lp0.eu>
---
 drivers/watchdog/Kconfig       |   1 +
 drivers/watchdog/bcm63xx_wdt.c | 249 ++++++++++++-----------------------------
 2 files changed, 74 insertions(+), 176 deletions(-)

diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index 7a8a6c6..6815b74 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -1273,6 +1273,7 @@ config OCTEON_WDT
 config BCM63XX_WDT
 	tristate "Broadcom BCM63xx hardware watchdog"
 	depends on BCM63XX
+	select WATCHDOG_CORE
 	help
 	  Watchdog driver for the built in watchdog hardware in Broadcom
 	  BCM63xx SoC.
diff --git a/drivers/watchdog/bcm63xx_wdt.c b/drivers/watchdog/bcm63xx_wdt.c
index f88fc97..1d2a501 100644
--- a/drivers/watchdog/bcm63xx_wdt.c
+++ b/drivers/watchdog/bcm63xx_wdt.c
@@ -13,20 +13,15 @@
 
 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
 
-#include <linux/bitops.h>
 #include <linux/errno.h>
-#include <linux/fs.h>
 #include <linux/io.h>
 #include <linux/kernel.h>
-#include <linux/miscdevice.h>
 #include <linux/module.h>
 #include <linux/moduleparam.h>
 #include <linux/spinlock.h>
 #include <linux/types.h>
-#include <linux/uaccess.h>
 #include <linux/watchdog.h>
 #include <linux/interrupt.h>
-#include <linux/ptrace.h>
 #include <linux/resource.h>
 #include <linux/platform_device.h>
 
@@ -38,53 +33,57 @@
 #define PFX KBUILD_MODNAME
 
 #define WDT_HZ			50000000		/* Fclk */
-#define WDT_DEFAULT_TIME	30			/* seconds */
-#define WDT_MAX_TIME		(0xffffffff / WDT_HZ)	/* seconds */
 
 struct bcm63xx_wdt_hw {
 	raw_spinlock_t lock;
 	void __iomem *regs;
-	unsigned long inuse;
 	bool running;
 };
-static struct bcm63xx_wdt_hw bcm63xx_wdt_device;
 
-static int expect_close;
-
-static int wdt_time = WDT_DEFAULT_TIME;
 static bool nowayout = WATCHDOG_NOWAYOUT;
 module_param(nowayout, bool, 0);
 MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
 	__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
 
-/* HW functions */
-static void bcm63xx_wdt_hw_start(void)
+static int bcm63xx_wdt_start(struct watchdog_device *wdd)
 {
+	struct bcm63xx_wdt_hw *hw = watchdog_get_drvdata(wdd);
 	unsigned long flags;
 
-	raw_spin_lock_irqsave(&bcm63xx_wdt_device.lock, flags);
-	bcm_writel(wdt_time * WDT_HZ, bcm63xx_wdt_device.regs + WDT_DEFVAL_REG);
-	bcm_writel(WDT_START_1, bcm63xx_wdt_device.regs + WDT_CTL_REG);
-	bcm_writel(WDT_START_2, bcm63xx_wdt_device.regs + WDT_CTL_REG);
-	bcm63xx_wdt_device.running = true;
-	raw_spin_unlock_irqrestore(&bcm63xx_wdt_device.lock, flags);
+	raw_spin_lock_irqsave(&hw->lock, flags);
+	bcm_writel(wdd->timeout * WDT_HZ, hw->regs + WDT_DEFVAL_REG);
+	bcm_writel(WDT_START_1, hw->regs + WDT_CTL_REG);
+	bcm_writel(WDT_START_2, hw->regs + WDT_CTL_REG);
+	hw->running = true;
+	raw_spin_unlock_irqrestore(&hw->lock, flags);
+	return 0;
 }
 
-static void bcm63xx_wdt_hw_stop(void)
+static int bcm63xx_wdt_stop(struct watchdog_device *wdd)
 {
+	struct bcm63xx_wdt_hw *hw = watchdog_get_drvdata(wdd);
 	unsigned long flags;
 
-	raw_spin_lock_irqsave(&bcm63xx_wdt_device.lock, flags);
-	bcm_writel(WDT_STOP_1, bcm63xx_wdt_device.regs + WDT_CTL_REG);
-	bcm_writel(WDT_STOP_2, bcm63xx_wdt_device.regs + WDT_CTL_REG);
-	bcm63xx_wdt_device.running = false;
-	raw_spin_unlock_irqrestore(&bcm63xx_wdt_device.lock, flags);
+	raw_spin_lock_irqsave(&hw->lock, flags);
+	bcm_writel(WDT_STOP_1, hw->regs + WDT_CTL_REG);
+	bcm_writel(WDT_STOP_2, hw->regs + WDT_CTL_REG);
+	hw->running = false;
+	raw_spin_unlock_irqrestore(&hw->lock, flags);
+	return 0;
+}
+
+static int bcm63xx_wdt_set_timeout(struct watchdog_device *wdd,
+	unsigned int timeout)
+{
+	wdd->timeout = timeout;
+	return bcm63xx_wdt_start(wdd);
 }
 
 /* The watchdog interrupt occurs when half the timeout is remaining */
 static void bcm63xx_wdt_isr(void *data)
 {
-	struct bcm63xx_wdt_hw *hw = &bcm63xx_wdt_device;
+	struct watchdog_device *wdd = data;
+	struct bcm63xx_wdt_hw *hw = watchdog_get_drvdata(wdd);
 	unsigned long flags;
 
 	raw_spin_lock_irqsave(&hw->lock, flags);
@@ -118,147 +117,36 @@ static void bcm63xx_wdt_isr(void *data)
 		}
 
 		ms = timeleft / (WDT_HZ / 1000);
-		pr_alert("warning timer fired, reboot in %ums\n", ms);
+		dev_alert(wdd->dev,
+			"warning timer fired, reboot in %ums\n", ms);
 	}
 	raw_spin_unlock_irqrestore(&hw->lock, flags);
 }
 
-static int bcm63xx_wdt_settimeout(int new_time)
-{
-	if ((new_time <= 0) || (new_time > WDT_MAX_TIME))
-		return -EINVAL;
-
-	wdt_time = new_time;
-
-	return 0;
-}
-
-static int bcm63xx_wdt_open(struct inode *inode, struct file *file)
-{
-	if (test_and_set_bit(0, &bcm63xx_wdt_device.inuse))
-		return -EBUSY;
-
-	bcm63xx_wdt_hw_start();
-	return nonseekable_open(inode, file);
-}
-
-static int bcm63xx_wdt_release(struct inode *inode, struct file *file)
-{
-	if (expect_close == 42)
-		bcm63xx_wdt_hw_stop();
-	else {
-		pr_crit("Unexpected close, not stopping watchdog!\n");
-		bcm63xx_wdt_hw_start();
-	}
-	clear_bit(0, &bcm63xx_wdt_device.inuse);
-	expect_close = 0;
-	return 0;
-}
-
-static ssize_t bcm63xx_wdt_write(struct file *file, const char *data,
-				size_t len, loff_t *ppos)
-{
-	if (len) {
-		if (!nowayout) {
-			size_t i;
-
-			/* In case it was set long ago */
-			expect_close = 0;
-
-			for (i = 0; i != len; i++) {
-				char c;
-				if (get_user(c, data + i))
-					return -EFAULT;
-				if (c == 'V')
-					expect_close = 42;
-			}
-		}
-		bcm63xx_wdt_hw_start();
-	}
-	return len;
-}
-
-static struct watchdog_info bcm63xx_wdt_info = {
-	.identity       = PFX,
-	.options        = WDIOF_SETTIMEOUT |
-				WDIOF_KEEPALIVEPING |
-				WDIOF_MAGICCLOSE,
-};
-
-
-static long bcm63xx_wdt_ioctl(struct file *file, unsigned int cmd,
-				unsigned long arg)
-{
-	void __user *argp = (void __user *)arg;
-	int __user *p = argp;
-	int new_value, retval = -EINVAL;
-
-	switch (cmd) {
-	case WDIOC_GETSUPPORT:
-		return copy_to_user(argp, &bcm63xx_wdt_info,
-			sizeof(bcm63xx_wdt_info)) ? -EFAULT : 0;
-
-	case WDIOC_GETSTATUS:
-	case WDIOC_GETBOOTSTATUS:
-		return put_user(0, p);
-
-	case WDIOC_SETOPTIONS:
-		if (get_user(new_value, p))
-			return -EFAULT;
-
-		if (new_value & WDIOS_DISABLECARD) {
-			bcm63xx_wdt_hw_stop();
-			retval = 0;
-		}
-		if (new_value & WDIOS_ENABLECARD) {
-			bcm63xx_wdt_hw_start();
-			retval = 0;
-		}
-
-		return retval;
-
-	case WDIOC_KEEPALIVE:
-		bcm63xx_wdt_hw_start();
-		return 0;
-
-	case WDIOC_SETTIMEOUT:
-		if (get_user(new_value, p))
-			return -EFAULT;
-
-		if (bcm63xx_wdt_settimeout(new_value))
-			return -EINVAL;
-
-		bcm63xx_wdt_hw_start();
-
-	case WDIOC_GETTIMEOUT:
-		return put_user(wdt_time, p);
-
-	default:
-		return -ENOTTY;
-
-	}
-}
-
-static const struct file_operations bcm63xx_wdt_fops = {
-	.owner		= THIS_MODULE,
-	.llseek		= no_llseek,
-	.write		= bcm63xx_wdt_write,
-	.unlocked_ioctl	= bcm63xx_wdt_ioctl,
-	.open		= bcm63xx_wdt_open,
-	.release	= bcm63xx_wdt_release,
+static struct watchdog_ops bcm63xx_wdt_ops = {
+	.owner = THIS_MODULE,
+	.start = bcm63xx_wdt_start,
+	.stop = bcm63xx_wdt_stop,
+	.set_timeout = bcm63xx_wdt_set_timeout,
 };
 
-static struct miscdevice bcm63xx_wdt_miscdev = {
-	.minor	= WATCHDOG_MINOR,
-	.name	= "watchdog",
-	.fops	= &bcm63xx_wdt_fops,
+static const struct watchdog_info bcm63xx_wdt_info = {
+	.options = WDIOC_GETTIMELEFT | WDIOF_SETTIMEOUT |
+			WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
+	.identity = "BCM63xx Watchdog",
 };
 
-
 static int bcm63xx_wdt_probe(struct platform_device *pdev)
 {
-	int ret;
+	struct bcm63xx_wdt_hw *hw;
+	struct watchdog_device *wdd;
 	struct resource *r;
+	int ret;
+
+	hw = devm_kzalloc(&pdev->dev, sizeof(*hw), GFP_KERNEL);
+	wdd = devm_kzalloc(&pdev->dev, sizeof(*wdd), GFP_KERNEL);
+	if (!hw || !wdd)
+		return -ENOMEM;
 
 	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 	if (!r) {
@@ -266,36 +154,44 @@ static int bcm63xx_wdt_probe(struct platform_device *pdev)
 		return -ENODEV;
 	}
 
-	bcm63xx_wdt_device.regs = devm_ioremap_nocache(&pdev->dev, r->start,
-							resource_size(r));
-	if (!bcm63xx_wdt_device.regs) {
+	hw->regs = devm_ioremap_nocache(&pdev->dev, r->start, resource_size(r));
+	if (!hw->regs) {
 		dev_err(&pdev->dev, "failed to remap I/O resources\n");
 		return -ENXIO;
 	}
 
-	raw_spin_lock_init(&bcm63xx_wdt_device.lock);
+	raw_spin_lock_init(&hw->lock);
+	hw->running = false;
+
+	wdd->parent = &pdev->dev;
+	wdd->ops = &bcm63xx_wdt_ops;
+	wdd->info = &bcm63xx_wdt_info;
+	wdd->min_timeout = 1;
+	wdd->max_timeout = 0xffffffff / WDT_HZ;
+	wdd->timeout = min(30U, wdd->max_timeout);
+
+	watchdog_set_drvdata(wdd, hw);
+	platform_set_drvdata(pdev, wdd);
+
+	watchdog_init_timeout(wdd, 0, &pdev->dev);
+	watchdog_set_nowayout(wdd, nowayout);
 
-	ret = bcm63xx_timer_register(TIMER_WDT_ID, bcm63xx_wdt_isr, NULL);
+	ret = bcm63xx_timer_register(TIMER_WDT_ID, bcm63xx_wdt_isr, wdd);
 	if (ret < 0) {
 		dev_err(&pdev->dev, "failed to register wdt timer isr\n");
 		return ret;
 	}
 
-	if (bcm63xx_wdt_settimeout(wdt_time)) {
-		bcm63xx_wdt_settimeout(WDT_DEFAULT_TIME);
-		dev_info(&pdev->dev,
-			": wdt_time value must be 1 <= wdt_time <= %d, using %d\n",
-			WDT_MAX_TIME, wdt_time);
-	}
-
-	ret = misc_register(&bcm63xx_wdt_miscdev);
+	ret = watchdog_register_device(wdd);
 	if (ret < 0) {
 		dev_err(&pdev->dev, "failed to register watchdog device\n");
 		goto unregister_timer;
 	}
 
-	dev_info(&pdev->dev, " started, timer margin: %d sec\n",
-						WDT_DEFAULT_TIME);
+	dev_info(&pdev->dev,
+		"%s at MMIO 0x%p (timeout = %us, max_timeout = %us)",
+		dev_name(wdd->dev), hw->regs,
+		wdd->timeout, wdd->max_timeout);
 
 	return 0;
 
@@ -306,17 +202,18 @@ unregister_timer:
 
 static int bcm63xx_wdt_remove(struct platform_device *pdev)
 {
-	if (!nowayout)
-		bcm63xx_wdt_hw_stop();
+	struct watchdog_device *wdd = platform_get_drvdata(pdev);
 
-	misc_deregister(&bcm63xx_wdt_miscdev);
 	bcm63xx_timer_unregister(TIMER_WDT_ID);
+	watchdog_unregister_device(wdd);
 	return 0;
 }
 
 static void bcm63xx_wdt_shutdown(struct platform_device *pdev)
 {
-	bcm63xx_wdt_hw_stop();
+	struct watchdog_device *wdd = platform_get_drvdata(pdev);
+
+	bcm63xx_wdt_stop(wdd);
 }
 
 static struct platform_driver bcm63xx_wdt_driver = {
-- 
2.1.4

-- 
Simon Arlott

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

* [PATCH 5/10] watchdog: bcm63xx_wdt: Use WATCHDOG_CORE
@ 2015-11-22 14:06             ` Simon Arlott
  0 siblings, 0 replies; 95+ messages in thread
From: Simon Arlott @ 2015-11-22 14:06 UTC (permalink / raw)
  To: Guenter Roeck, devicetree-u79uwXL29TY76Z2rM5mHXA, Ralf Baechle,
	Thomas Gleixner, Jason Cooper, Marc Zyngier, Kevin Cernekee,
	Florian Fainelli, Wim Van Sebroeck, Maxime Bizon,
	Linux Kernel Mailing List, linux-mips-6z/3iImG2C8G8FEW9MqTrA,
	linux-watchdog-u79uwXL29TY76Z2rM5mHXA
  Cc: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
	Jonas Gorski

Convert bcm63xx_wdt to use WATCHDOG_CORE.

The default and maximum time constants that are only used once have been
moved to the initialisation of the struct watchdog_device.

Signed-off-by: Simon Arlott <simon-A6De1vDTPLDsq35pWSNszA@public.gmane.org>
---
 drivers/watchdog/Kconfig       |   1 +
 drivers/watchdog/bcm63xx_wdt.c | 249 ++++++++++++-----------------------------
 2 files changed, 74 insertions(+), 176 deletions(-)

diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index 7a8a6c6..6815b74 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -1273,6 +1273,7 @@ config OCTEON_WDT
 config BCM63XX_WDT
 	tristate "Broadcom BCM63xx hardware watchdog"
 	depends on BCM63XX
+	select WATCHDOG_CORE
 	help
 	  Watchdog driver for the built in watchdog hardware in Broadcom
 	  BCM63xx SoC.
diff --git a/drivers/watchdog/bcm63xx_wdt.c b/drivers/watchdog/bcm63xx_wdt.c
index f88fc97..1d2a501 100644
--- a/drivers/watchdog/bcm63xx_wdt.c
+++ b/drivers/watchdog/bcm63xx_wdt.c
@@ -13,20 +13,15 @@
 
 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
 
-#include <linux/bitops.h>
 #include <linux/errno.h>
-#include <linux/fs.h>
 #include <linux/io.h>
 #include <linux/kernel.h>
-#include <linux/miscdevice.h>
 #include <linux/module.h>
 #include <linux/moduleparam.h>
 #include <linux/spinlock.h>
 #include <linux/types.h>
-#include <linux/uaccess.h>
 #include <linux/watchdog.h>
 #include <linux/interrupt.h>
-#include <linux/ptrace.h>
 #include <linux/resource.h>
 #include <linux/platform_device.h>
 
@@ -38,53 +33,57 @@
 #define PFX KBUILD_MODNAME
 
 #define WDT_HZ			50000000		/* Fclk */
-#define WDT_DEFAULT_TIME	30			/* seconds */
-#define WDT_MAX_TIME		(0xffffffff / WDT_HZ)	/* seconds */
 
 struct bcm63xx_wdt_hw {
 	raw_spinlock_t lock;
 	void __iomem *regs;
-	unsigned long inuse;
 	bool running;
 };
-static struct bcm63xx_wdt_hw bcm63xx_wdt_device;
 
-static int expect_close;
-
-static int wdt_time = WDT_DEFAULT_TIME;
 static bool nowayout = WATCHDOG_NOWAYOUT;
 module_param(nowayout, bool, 0);
 MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
 	__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
 
-/* HW functions */
-static void bcm63xx_wdt_hw_start(void)
+static int bcm63xx_wdt_start(struct watchdog_device *wdd)
 {
+	struct bcm63xx_wdt_hw *hw = watchdog_get_drvdata(wdd);
 	unsigned long flags;
 
-	raw_spin_lock_irqsave(&bcm63xx_wdt_device.lock, flags);
-	bcm_writel(wdt_time * WDT_HZ, bcm63xx_wdt_device.regs + WDT_DEFVAL_REG);
-	bcm_writel(WDT_START_1, bcm63xx_wdt_device.regs + WDT_CTL_REG);
-	bcm_writel(WDT_START_2, bcm63xx_wdt_device.regs + WDT_CTL_REG);
-	bcm63xx_wdt_device.running = true;
-	raw_spin_unlock_irqrestore(&bcm63xx_wdt_device.lock, flags);
+	raw_spin_lock_irqsave(&hw->lock, flags);
+	bcm_writel(wdd->timeout * WDT_HZ, hw->regs + WDT_DEFVAL_REG);
+	bcm_writel(WDT_START_1, hw->regs + WDT_CTL_REG);
+	bcm_writel(WDT_START_2, hw->regs + WDT_CTL_REG);
+	hw->running = true;
+	raw_spin_unlock_irqrestore(&hw->lock, flags);
+	return 0;
 }
 
-static void bcm63xx_wdt_hw_stop(void)
+static int bcm63xx_wdt_stop(struct watchdog_device *wdd)
 {
+	struct bcm63xx_wdt_hw *hw = watchdog_get_drvdata(wdd);
 	unsigned long flags;
 
-	raw_spin_lock_irqsave(&bcm63xx_wdt_device.lock, flags);
-	bcm_writel(WDT_STOP_1, bcm63xx_wdt_device.regs + WDT_CTL_REG);
-	bcm_writel(WDT_STOP_2, bcm63xx_wdt_device.regs + WDT_CTL_REG);
-	bcm63xx_wdt_device.running = false;
-	raw_spin_unlock_irqrestore(&bcm63xx_wdt_device.lock, flags);
+	raw_spin_lock_irqsave(&hw->lock, flags);
+	bcm_writel(WDT_STOP_1, hw->regs + WDT_CTL_REG);
+	bcm_writel(WDT_STOP_2, hw->regs + WDT_CTL_REG);
+	hw->running = false;
+	raw_spin_unlock_irqrestore(&hw->lock, flags);
+	return 0;
+}
+
+static int bcm63xx_wdt_set_timeout(struct watchdog_device *wdd,
+	unsigned int timeout)
+{
+	wdd->timeout = timeout;
+	return bcm63xx_wdt_start(wdd);
 }
 
 /* The watchdog interrupt occurs when half the timeout is remaining */
 static void bcm63xx_wdt_isr(void *data)
 {
-	struct bcm63xx_wdt_hw *hw = &bcm63xx_wdt_device;
+	struct watchdog_device *wdd = data;
+	struct bcm63xx_wdt_hw *hw = watchdog_get_drvdata(wdd);
 	unsigned long flags;
 
 	raw_spin_lock_irqsave(&hw->lock, flags);
@@ -118,147 +117,36 @@ static void bcm63xx_wdt_isr(void *data)
 		}
 
 		ms = timeleft / (WDT_HZ / 1000);
-		pr_alert("warning timer fired, reboot in %ums\n", ms);
+		dev_alert(wdd->dev,
+			"warning timer fired, reboot in %ums\n", ms);
 	}
 	raw_spin_unlock_irqrestore(&hw->lock, flags);
 }
 
-static int bcm63xx_wdt_settimeout(int new_time)
-{
-	if ((new_time <= 0) || (new_time > WDT_MAX_TIME))
-		return -EINVAL;
-
-	wdt_time = new_time;
-
-	return 0;
-}
-
-static int bcm63xx_wdt_open(struct inode *inode, struct file *file)
-{
-	if (test_and_set_bit(0, &bcm63xx_wdt_device.inuse))
-		return -EBUSY;
-
-	bcm63xx_wdt_hw_start();
-	return nonseekable_open(inode, file);
-}
-
-static int bcm63xx_wdt_release(struct inode *inode, struct file *file)
-{
-	if (expect_close == 42)
-		bcm63xx_wdt_hw_stop();
-	else {
-		pr_crit("Unexpected close, not stopping watchdog!\n");
-		bcm63xx_wdt_hw_start();
-	}
-	clear_bit(0, &bcm63xx_wdt_device.inuse);
-	expect_close = 0;
-	return 0;
-}
-
-static ssize_t bcm63xx_wdt_write(struct file *file, const char *data,
-				size_t len, loff_t *ppos)
-{
-	if (len) {
-		if (!nowayout) {
-			size_t i;
-
-			/* In case it was set long ago */
-			expect_close = 0;
-
-			for (i = 0; i != len; i++) {
-				char c;
-				if (get_user(c, data + i))
-					return -EFAULT;
-				if (c == 'V')
-					expect_close = 42;
-			}
-		}
-		bcm63xx_wdt_hw_start();
-	}
-	return len;
-}
-
-static struct watchdog_info bcm63xx_wdt_info = {
-	.identity       = PFX,
-	.options        = WDIOF_SETTIMEOUT |
-				WDIOF_KEEPALIVEPING |
-				WDIOF_MAGICCLOSE,
-};
-
-
-static long bcm63xx_wdt_ioctl(struct file *file, unsigned int cmd,
-				unsigned long arg)
-{
-	void __user *argp = (void __user *)arg;
-	int __user *p = argp;
-	int new_value, retval = -EINVAL;
-
-	switch (cmd) {
-	case WDIOC_GETSUPPORT:
-		return copy_to_user(argp, &bcm63xx_wdt_info,
-			sizeof(bcm63xx_wdt_info)) ? -EFAULT : 0;
-
-	case WDIOC_GETSTATUS:
-	case WDIOC_GETBOOTSTATUS:
-		return put_user(0, p);
-
-	case WDIOC_SETOPTIONS:
-		if (get_user(new_value, p))
-			return -EFAULT;
-
-		if (new_value & WDIOS_DISABLECARD) {
-			bcm63xx_wdt_hw_stop();
-			retval = 0;
-		}
-		if (new_value & WDIOS_ENABLECARD) {
-			bcm63xx_wdt_hw_start();
-			retval = 0;
-		}
-
-		return retval;
-
-	case WDIOC_KEEPALIVE:
-		bcm63xx_wdt_hw_start();
-		return 0;
-
-	case WDIOC_SETTIMEOUT:
-		if (get_user(new_value, p))
-			return -EFAULT;
-
-		if (bcm63xx_wdt_settimeout(new_value))
-			return -EINVAL;
-
-		bcm63xx_wdt_hw_start();
-
-	case WDIOC_GETTIMEOUT:
-		return put_user(wdt_time, p);
-
-	default:
-		return -ENOTTY;
-
-	}
-}
-
-static const struct file_operations bcm63xx_wdt_fops = {
-	.owner		= THIS_MODULE,
-	.llseek		= no_llseek,
-	.write		= bcm63xx_wdt_write,
-	.unlocked_ioctl	= bcm63xx_wdt_ioctl,
-	.open		= bcm63xx_wdt_open,
-	.release	= bcm63xx_wdt_release,
+static struct watchdog_ops bcm63xx_wdt_ops = {
+	.owner = THIS_MODULE,
+	.start = bcm63xx_wdt_start,
+	.stop = bcm63xx_wdt_stop,
+	.set_timeout = bcm63xx_wdt_set_timeout,
 };
 
-static struct miscdevice bcm63xx_wdt_miscdev = {
-	.minor	= WATCHDOG_MINOR,
-	.name	= "watchdog",
-	.fops	= &bcm63xx_wdt_fops,
+static const struct watchdog_info bcm63xx_wdt_info = {
+	.options = WDIOC_GETTIMELEFT | WDIOF_SETTIMEOUT |
+			WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
+	.identity = "BCM63xx Watchdog",
 };
 
-
 static int bcm63xx_wdt_probe(struct platform_device *pdev)
 {
-	int ret;
+	struct bcm63xx_wdt_hw *hw;
+	struct watchdog_device *wdd;
 	struct resource *r;
+	int ret;
+
+	hw = devm_kzalloc(&pdev->dev, sizeof(*hw), GFP_KERNEL);
+	wdd = devm_kzalloc(&pdev->dev, sizeof(*wdd), GFP_KERNEL);
+	if (!hw || !wdd)
+		return -ENOMEM;
 
 	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 	if (!r) {
@@ -266,36 +154,44 @@ static int bcm63xx_wdt_probe(struct platform_device *pdev)
 		return -ENODEV;
 	}
 
-	bcm63xx_wdt_device.regs = devm_ioremap_nocache(&pdev->dev, r->start,
-							resource_size(r));
-	if (!bcm63xx_wdt_device.regs) {
+	hw->regs = devm_ioremap_nocache(&pdev->dev, r->start, resource_size(r));
+	if (!hw->regs) {
 		dev_err(&pdev->dev, "failed to remap I/O resources\n");
 		return -ENXIO;
 	}
 
-	raw_spin_lock_init(&bcm63xx_wdt_device.lock);
+	raw_spin_lock_init(&hw->lock);
+	hw->running = false;
+
+	wdd->parent = &pdev->dev;
+	wdd->ops = &bcm63xx_wdt_ops;
+	wdd->info = &bcm63xx_wdt_info;
+	wdd->min_timeout = 1;
+	wdd->max_timeout = 0xffffffff / WDT_HZ;
+	wdd->timeout = min(30U, wdd->max_timeout);
+
+	watchdog_set_drvdata(wdd, hw);
+	platform_set_drvdata(pdev, wdd);
+
+	watchdog_init_timeout(wdd, 0, &pdev->dev);
+	watchdog_set_nowayout(wdd, nowayout);
 
-	ret = bcm63xx_timer_register(TIMER_WDT_ID, bcm63xx_wdt_isr, NULL);
+	ret = bcm63xx_timer_register(TIMER_WDT_ID, bcm63xx_wdt_isr, wdd);
 	if (ret < 0) {
 		dev_err(&pdev->dev, "failed to register wdt timer isr\n");
 		return ret;
 	}
 
-	if (bcm63xx_wdt_settimeout(wdt_time)) {
-		bcm63xx_wdt_settimeout(WDT_DEFAULT_TIME);
-		dev_info(&pdev->dev,
-			": wdt_time value must be 1 <= wdt_time <= %d, using %d\n",
-			WDT_MAX_TIME, wdt_time);
-	}
-
-	ret = misc_register(&bcm63xx_wdt_miscdev);
+	ret = watchdog_register_device(wdd);
 	if (ret < 0) {
 		dev_err(&pdev->dev, "failed to register watchdog device\n");
 		goto unregister_timer;
 	}
 
-	dev_info(&pdev->dev, " started, timer margin: %d sec\n",
-						WDT_DEFAULT_TIME);
+	dev_info(&pdev->dev,
+		"%s at MMIO 0x%p (timeout = %us, max_timeout = %us)",
+		dev_name(wdd->dev), hw->regs,
+		wdd->timeout, wdd->max_timeout);
 
 	return 0;
 
@@ -306,17 +202,18 @@ unregister_timer:
 
 static int bcm63xx_wdt_remove(struct platform_device *pdev)
 {
-	if (!nowayout)
-		bcm63xx_wdt_hw_stop();
+	struct watchdog_device *wdd = platform_get_drvdata(pdev);
 
-	misc_deregister(&bcm63xx_wdt_miscdev);
 	bcm63xx_timer_unregister(TIMER_WDT_ID);
+	watchdog_unregister_device(wdd);
 	return 0;
 }
 
 static void bcm63xx_wdt_shutdown(struct platform_device *pdev)
 {
-	bcm63xx_wdt_hw_stop();
+	struct watchdog_device *wdd = platform_get_drvdata(pdev);
+
+	bcm63xx_wdt_stop(wdd);
 }
 
 static struct platform_driver bcm63xx_wdt_driver = {
-- 
2.1.4

-- 
Simon Arlott
--
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] 95+ messages in thread

* [PATCH 6/10] watchdog: bcm63xx_wdt: Obtain watchdog clock HZ from "periph" clk
@ 2015-11-22 14:07             ` Simon Arlott
  0 siblings, 0 replies; 95+ messages in thread
From: Simon Arlott @ 2015-11-22 14:07 UTC (permalink / raw)
  To: Guenter Roeck, devicetree, Ralf Baechle, Thomas Gleixner,
	Jason Cooper, Marc Zyngier, Kevin Cernekee, Florian Fainelli,
	Wim Van Sebroeck, Maxime Bizon, Linux Kernel Mailing List,
	linux-mips, linux-watchdog
  Cc: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
	Jonas Gorski

Instead of using a fixed clock HZ in the driver, obtain it from the
"periph" clk that the watchdog timer uses.

Signed-off-by: Simon Arlott <simon@fire.lp0.eu>
---
 drivers/watchdog/bcm63xx_wdt.c | 36 +++++++++++++++++++++++++++++++-----
 1 file changed, 31 insertions(+), 5 deletions(-)

diff --git a/drivers/watchdog/bcm63xx_wdt.c b/drivers/watchdog/bcm63xx_wdt.c
index 1d2a501..eb5e551 100644
--- a/drivers/watchdog/bcm63xx_wdt.c
+++ b/drivers/watchdog/bcm63xx_wdt.c
@@ -13,6 +13,7 @@
 
 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
 
+#include <linux/clk.h>
 #include <linux/errno.h>
 #include <linux/io.h>
 #include <linux/kernel.h>
@@ -32,11 +33,13 @@
 
 #define PFX KBUILD_MODNAME
 
-#define WDT_HZ			50000000		/* Fclk */
+#define WDT_CLK_NAME		"periph"
 
 struct bcm63xx_wdt_hw {
 	raw_spinlock_t lock;
 	void __iomem *regs;
+	struct clk *clk;
+	unsigned long clock_hz;
 	bool running;
 };
 
@@ -51,7 +54,7 @@ static int bcm63xx_wdt_start(struct watchdog_device *wdd)
 	unsigned long flags;
 
 	raw_spin_lock_irqsave(&hw->lock, flags);
-	bcm_writel(wdd->timeout * WDT_HZ, hw->regs + WDT_DEFVAL_REG);
+	bcm_writel(wdd->timeout * hw->clock_hz, hw->regs + WDT_DEFVAL_REG);
 	bcm_writel(WDT_START_1, hw->regs + WDT_CTL_REG);
 	bcm_writel(WDT_START_2, hw->regs + WDT_CTL_REG);
 	hw->running = true;
@@ -116,7 +119,7 @@ static void bcm63xx_wdt_isr(void *data)
 			die(PFX ": watchdog timer expired\n", get_irq_regs());
 		}
 
-		ms = timeleft / (WDT_HZ / 1000);
+		ms = timeleft / (hw->clock_hz / 1000);
 		dev_alert(wdd->dev,
 			"warning timer fired, reboot in %ums\n", ms);
 	}
@@ -160,6 +163,24 @@ static int bcm63xx_wdt_probe(struct platform_device *pdev)
 		return -ENXIO;
 	}
 
+	hw->clk = devm_clk_get(&pdev->dev, WDT_CLK_NAME);
+	if (IS_ERR(hw->clk)) {
+		dev_err(&pdev->dev, "unable to request clock\n");
+		return PTR_ERR(hw->clk);
+	}
+
+	hw->clock_hz = clk_get_rate(hw->clk);
+	if (!hw->clock_hz) {
+		dev_err(&pdev->dev, "unable to fetch clock rate\n");
+		return -EINVAL;
+	}
+
+	ret = clk_prepare_enable(hw->clk);
+	if (ret) {
+		dev_err(&pdev->dev, "unable to enable clock\n");
+		return ret;
+	}
+
 	raw_spin_lock_init(&hw->lock);
 	hw->running = false;
 
@@ -167,7 +188,7 @@ static int bcm63xx_wdt_probe(struct platform_device *pdev)
 	wdd->ops = &bcm63xx_wdt_ops;
 	wdd->info = &bcm63xx_wdt_info;
 	wdd->min_timeout = 1;
-	wdd->max_timeout = 0xffffffff / WDT_HZ;
+	wdd->max_timeout = 0xffffffff / hw->clock_hz;
 	wdd->timeout = min(30U, wdd->max_timeout);
 
 	watchdog_set_drvdata(wdd, hw);
@@ -179,7 +200,7 @@ static int bcm63xx_wdt_probe(struct platform_device *pdev)
 	ret = bcm63xx_timer_register(TIMER_WDT_ID, bcm63xx_wdt_isr, wdd);
 	if (ret < 0) {
 		dev_err(&pdev->dev, "failed to register wdt timer isr\n");
-		return ret;
+		goto disable_clk;
 	}
 
 	ret = watchdog_register_device(wdd);
@@ -197,15 +218,20 @@ static int bcm63xx_wdt_probe(struct platform_device *pdev)
 
 unregister_timer:
 	bcm63xx_timer_unregister(TIMER_WDT_ID);
+
+disable_clk:
+	clk_disable_unprepare(hw->clk);
 	return ret;
 }
 
 static int bcm63xx_wdt_remove(struct platform_device *pdev)
 {
 	struct watchdog_device *wdd = platform_get_drvdata(pdev);
+	struct bcm63xx_wdt_hw *hw = watchdog_get_drvdata(wdd);
 
 	bcm63xx_timer_unregister(TIMER_WDT_ID);
 	watchdog_unregister_device(wdd);
+	clk_disable_unprepare(hw->clk);
 	return 0;
 }
 
-- 
2.1.4

-- 
Simon Arlott

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

* [PATCH 6/10] watchdog: bcm63xx_wdt: Obtain watchdog clock HZ from "periph" clk
@ 2015-11-22 14:07             ` Simon Arlott
  0 siblings, 0 replies; 95+ messages in thread
From: Simon Arlott @ 2015-11-22 14:07 UTC (permalink / raw)
  To: Guenter Roeck, devicetree-u79uwXL29TY76Z2rM5mHXA, Ralf Baechle,
	Thomas Gleixner, Jason Cooper, Marc Zyngier, Kevin Cernekee,
	Florian Fainelli, Wim Van Sebroeck, Maxime Bizon,
	Linux Kernel Mailing List, linux-mips-6z/3iImG2C8G8FEW9MqTrA,
	linux-watchdog-u79uwXL29TY76Z2rM5mHXA
  Cc: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
	Jonas Gorski

Instead of using a fixed clock HZ in the driver, obtain it from the
"periph" clk that the watchdog timer uses.

Signed-off-by: Simon Arlott <simon-A6De1vDTPLDsq35pWSNszA@public.gmane.org>
---
 drivers/watchdog/bcm63xx_wdt.c | 36 +++++++++++++++++++++++++++++++-----
 1 file changed, 31 insertions(+), 5 deletions(-)

diff --git a/drivers/watchdog/bcm63xx_wdt.c b/drivers/watchdog/bcm63xx_wdt.c
index 1d2a501..eb5e551 100644
--- a/drivers/watchdog/bcm63xx_wdt.c
+++ b/drivers/watchdog/bcm63xx_wdt.c
@@ -13,6 +13,7 @@
 
 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
 
+#include <linux/clk.h>
 #include <linux/errno.h>
 #include <linux/io.h>
 #include <linux/kernel.h>
@@ -32,11 +33,13 @@
 
 #define PFX KBUILD_MODNAME
 
-#define WDT_HZ			50000000		/* Fclk */
+#define WDT_CLK_NAME		"periph"
 
 struct bcm63xx_wdt_hw {
 	raw_spinlock_t lock;
 	void __iomem *regs;
+	struct clk *clk;
+	unsigned long clock_hz;
 	bool running;
 };
 
@@ -51,7 +54,7 @@ static int bcm63xx_wdt_start(struct watchdog_device *wdd)
 	unsigned long flags;
 
 	raw_spin_lock_irqsave(&hw->lock, flags);
-	bcm_writel(wdd->timeout * WDT_HZ, hw->regs + WDT_DEFVAL_REG);
+	bcm_writel(wdd->timeout * hw->clock_hz, hw->regs + WDT_DEFVAL_REG);
 	bcm_writel(WDT_START_1, hw->regs + WDT_CTL_REG);
 	bcm_writel(WDT_START_2, hw->regs + WDT_CTL_REG);
 	hw->running = true;
@@ -116,7 +119,7 @@ static void bcm63xx_wdt_isr(void *data)
 			die(PFX ": watchdog timer expired\n", get_irq_regs());
 		}
 
-		ms = timeleft / (WDT_HZ / 1000);
+		ms = timeleft / (hw->clock_hz / 1000);
 		dev_alert(wdd->dev,
 			"warning timer fired, reboot in %ums\n", ms);
 	}
@@ -160,6 +163,24 @@ static int bcm63xx_wdt_probe(struct platform_device *pdev)
 		return -ENXIO;
 	}
 
+	hw->clk = devm_clk_get(&pdev->dev, WDT_CLK_NAME);
+	if (IS_ERR(hw->clk)) {
+		dev_err(&pdev->dev, "unable to request clock\n");
+		return PTR_ERR(hw->clk);
+	}
+
+	hw->clock_hz = clk_get_rate(hw->clk);
+	if (!hw->clock_hz) {
+		dev_err(&pdev->dev, "unable to fetch clock rate\n");
+		return -EINVAL;
+	}
+
+	ret = clk_prepare_enable(hw->clk);
+	if (ret) {
+		dev_err(&pdev->dev, "unable to enable clock\n");
+		return ret;
+	}
+
 	raw_spin_lock_init(&hw->lock);
 	hw->running = false;
 
@@ -167,7 +188,7 @@ static int bcm63xx_wdt_probe(struct platform_device *pdev)
 	wdd->ops = &bcm63xx_wdt_ops;
 	wdd->info = &bcm63xx_wdt_info;
 	wdd->min_timeout = 1;
-	wdd->max_timeout = 0xffffffff / WDT_HZ;
+	wdd->max_timeout = 0xffffffff / hw->clock_hz;
 	wdd->timeout = min(30U, wdd->max_timeout);
 
 	watchdog_set_drvdata(wdd, hw);
@@ -179,7 +200,7 @@ static int bcm63xx_wdt_probe(struct platform_device *pdev)
 	ret = bcm63xx_timer_register(TIMER_WDT_ID, bcm63xx_wdt_isr, wdd);
 	if (ret < 0) {
 		dev_err(&pdev->dev, "failed to register wdt timer isr\n");
-		return ret;
+		goto disable_clk;
 	}
 
 	ret = watchdog_register_device(wdd);
@@ -197,15 +218,20 @@ static int bcm63xx_wdt_probe(struct platform_device *pdev)
 
 unregister_timer:
 	bcm63xx_timer_unregister(TIMER_WDT_ID);
+
+disable_clk:
+	clk_disable_unprepare(hw->clk);
 	return ret;
 }
 
 static int bcm63xx_wdt_remove(struct platform_device *pdev)
 {
 	struct watchdog_device *wdd = platform_get_drvdata(pdev);
+	struct bcm63xx_wdt_hw *hw = watchdog_get_drvdata(wdd);
 
 	bcm63xx_timer_unregister(TIMER_WDT_ID);
 	watchdog_unregister_device(wdd);
+	clk_disable_unprepare(hw->clk);
 	return 0;
 }
 
-- 
2.1.4

-- 
Simon Arlott
--
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] 95+ messages in thread

* [PATCH 7/10] watchdog: bcm63xx_wdt: Add get_timeleft function
@ 2015-11-22 14:09             ` Simon Arlott
  0 siblings, 0 replies; 95+ messages in thread
From: Simon Arlott @ 2015-11-22 14:09 UTC (permalink / raw)
  To: Guenter Roeck, devicetree, Ralf Baechle, Thomas Gleixner,
	Jason Cooper, Marc Zyngier, Kevin Cernekee, Florian Fainelli,
	Wim Van Sebroeck, Maxime Bizon, Linux Kernel Mailing List,
	linux-mips, linux-watchdog
  Cc: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
	Jonas Gorski

Return the remaining time from the hardware control register.

Warn when the device is registered if the hardware watchdog is currently
running and report the remaining time left.

Signed-off-by: Simon Arlott <simon@fire.lp0.eu>
---
 drivers/watchdog/bcm63xx_wdt.c | 38 +++++++++++++++++++++++++++++++++++++-
 1 file changed, 37 insertions(+), 1 deletion(-)

diff --git a/drivers/watchdog/bcm63xx_wdt.c b/drivers/watchdog/bcm63xx_wdt.c
index eb5e551..7109eb4 100644
--- a/drivers/watchdog/bcm63xx_wdt.c
+++ b/drivers/watchdog/bcm63xx_wdt.c
@@ -14,6 +14,7 @@
 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
 
 #include <linux/clk.h>
+#include <linux/delay.h>
 #include <linux/errno.h>
 #include <linux/io.h>
 #include <linux/kernel.h>
@@ -75,6 +76,19 @@ static int bcm63xx_wdt_stop(struct watchdog_device *wdd)
 	return 0;
 }
 
+static unsigned int bcm63xx_wdt_get_timeleft(struct watchdog_device *wdd)
+{
+	struct bcm63xx_wdt_hw *hw = watchdog_get_drvdata(wdd);
+	unsigned long flags;
+	u32 val;
+
+	raw_spin_lock_irqsave(&hw->lock, flags);
+	val = __raw_readl(hw->regs + WDT_CTL_REG);
+	val /= hw->clock_hz;
+	raw_spin_unlock_irqrestore(&hw->lock, flags);
+	return val;
+}
+
 static int bcm63xx_wdt_set_timeout(struct watchdog_device *wdd,
 	unsigned int timeout)
 {
@@ -130,6 +144,7 @@ static struct watchdog_ops bcm63xx_wdt_ops = {
 	.owner = THIS_MODULE,
 	.start = bcm63xx_wdt_start,
 	.stop = bcm63xx_wdt_stop,
+	.get_timeleft = bcm63xx_wdt_get_timeleft,
 	.set_timeout = bcm63xx_wdt_set_timeout,
 };
 
@@ -144,6 +159,8 @@ static int bcm63xx_wdt_probe(struct platform_device *pdev)
 	struct bcm63xx_wdt_hw *hw;
 	struct watchdog_device *wdd;
 	struct resource *r;
+	u32 timeleft1, timeleft2;
+	unsigned int timeleft;
 	int ret;
 
 	hw = devm_kzalloc(&pdev->dev, sizeof(*hw), GFP_KERNEL);
@@ -182,7 +199,6 @@ static int bcm63xx_wdt_probe(struct platform_device *pdev)
 	}
 
 	raw_spin_lock_init(&hw->lock);
-	hw->running = false;
 
 	wdd->parent = &pdev->dev;
 	wdd->ops = &bcm63xx_wdt_ops;
@@ -197,6 +213,23 @@ static int bcm63xx_wdt_probe(struct platform_device *pdev)
 	watchdog_init_timeout(wdd, 0, &pdev->dev);
 	watchdog_set_nowayout(wdd, nowayout);
 
+	/* Compare two reads of the time left value, 2 clock ticks apart */
+	rmb();
+	timeleft1 = __raw_readl(hw->regs + WDT_CTL_REG);
+	udelay(DIV_ROUND_UP(1000000, hw->clock_hz / 2));
+	/* Ensure the register is read twice */
+	rmb();
+	timeleft2 = __raw_readl(hw->regs + WDT_CTL_REG);
+
+	/* If the time left is changing, the watchdog is running */
+	if (timeleft1 != timeleft2) {
+		hw->running = true;
+		timeleft = bcm63xx_wdt_get_timeleft(wdd);
+	} else {
+		hw->running = false;
+		timeleft = 0;
+	}
+
 	ret = bcm63xx_timer_register(TIMER_WDT_ID, bcm63xx_wdt_isr, wdd);
 	if (ret < 0) {
 		dev_err(&pdev->dev, "failed to register wdt timer isr\n");
@@ -214,6 +247,8 @@ static int bcm63xx_wdt_probe(struct platform_device *pdev)
 		dev_name(wdd->dev), hw->regs,
 		wdd->timeout, wdd->max_timeout);
 
+	if (timeleft > 0)
+		dev_alert(wdd->dev, "running, reboot in %us\n", timeleft);
 	return 0;
 
 unregister_timer:
@@ -255,6 +290,7 @@ module_platform_driver(bcm63xx_wdt_driver);
 
 MODULE_AUTHOR("Miguel Gaio <miguel.gaio@efixo.com>");
 MODULE_AUTHOR("Florian Fainelli <florian@openwrt.org>");
+MODULE_AUTHOR("Simon Arlott");
 MODULE_DESCRIPTION("Driver for the Broadcom BCM63xx SoC watchdog");
 MODULE_LICENSE("GPL");
 MODULE_ALIAS("platform:bcm63xx-wdt");
-- 
2.1.4

-- 
Simon Arlott

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

* [PATCH 7/10] watchdog: bcm63xx_wdt: Add get_timeleft function
@ 2015-11-22 14:09             ` Simon Arlott
  0 siblings, 0 replies; 95+ messages in thread
From: Simon Arlott @ 2015-11-22 14:09 UTC (permalink / raw)
  To: Guenter Roeck, devicetree-u79uwXL29TY76Z2rM5mHXA, Ralf Baechle,
	Thomas Gleixner, Jason Cooper, Marc Zyngier, Kevin Cernekee,
	Florian Fainelli, Wim Van Sebroeck, Maxime Bizon,
	Linux Kernel Mailing List, linux-mips-6z/3iImG2C8G8FEW9MqTrA,
	linux-watchdog-u79uwXL29TY76Z2rM5mHXA
  Cc: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
	Jonas Gorski

Return the remaining time from the hardware control register.

Warn when the device is registered if the hardware watchdog is currently
running and report the remaining time left.

Signed-off-by: Simon Arlott <simon-A6De1vDTPLDsq35pWSNszA@public.gmane.org>
---
 drivers/watchdog/bcm63xx_wdt.c | 38 +++++++++++++++++++++++++++++++++++++-
 1 file changed, 37 insertions(+), 1 deletion(-)

diff --git a/drivers/watchdog/bcm63xx_wdt.c b/drivers/watchdog/bcm63xx_wdt.c
index eb5e551..7109eb4 100644
--- a/drivers/watchdog/bcm63xx_wdt.c
+++ b/drivers/watchdog/bcm63xx_wdt.c
@@ -14,6 +14,7 @@
 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
 
 #include <linux/clk.h>
+#include <linux/delay.h>
 #include <linux/errno.h>
 #include <linux/io.h>
 #include <linux/kernel.h>
@@ -75,6 +76,19 @@ static int bcm63xx_wdt_stop(struct watchdog_device *wdd)
 	return 0;
 }
 
+static unsigned int bcm63xx_wdt_get_timeleft(struct watchdog_device *wdd)
+{
+	struct bcm63xx_wdt_hw *hw = watchdog_get_drvdata(wdd);
+	unsigned long flags;
+	u32 val;
+
+	raw_spin_lock_irqsave(&hw->lock, flags);
+	val = __raw_readl(hw->regs + WDT_CTL_REG);
+	val /= hw->clock_hz;
+	raw_spin_unlock_irqrestore(&hw->lock, flags);
+	return val;
+}
+
 static int bcm63xx_wdt_set_timeout(struct watchdog_device *wdd,
 	unsigned int timeout)
 {
@@ -130,6 +144,7 @@ static struct watchdog_ops bcm63xx_wdt_ops = {
 	.owner = THIS_MODULE,
 	.start = bcm63xx_wdt_start,
 	.stop = bcm63xx_wdt_stop,
+	.get_timeleft = bcm63xx_wdt_get_timeleft,
 	.set_timeout = bcm63xx_wdt_set_timeout,
 };
 
@@ -144,6 +159,8 @@ static int bcm63xx_wdt_probe(struct platform_device *pdev)
 	struct bcm63xx_wdt_hw *hw;
 	struct watchdog_device *wdd;
 	struct resource *r;
+	u32 timeleft1, timeleft2;
+	unsigned int timeleft;
 	int ret;
 
 	hw = devm_kzalloc(&pdev->dev, sizeof(*hw), GFP_KERNEL);
@@ -182,7 +199,6 @@ static int bcm63xx_wdt_probe(struct platform_device *pdev)
 	}
 
 	raw_spin_lock_init(&hw->lock);
-	hw->running = false;
 
 	wdd->parent = &pdev->dev;
 	wdd->ops = &bcm63xx_wdt_ops;
@@ -197,6 +213,23 @@ static int bcm63xx_wdt_probe(struct platform_device *pdev)
 	watchdog_init_timeout(wdd, 0, &pdev->dev);
 	watchdog_set_nowayout(wdd, nowayout);
 
+	/* Compare two reads of the time left value, 2 clock ticks apart */
+	rmb();
+	timeleft1 = __raw_readl(hw->regs + WDT_CTL_REG);
+	udelay(DIV_ROUND_UP(1000000, hw->clock_hz / 2));
+	/* Ensure the register is read twice */
+	rmb();
+	timeleft2 = __raw_readl(hw->regs + WDT_CTL_REG);
+
+	/* If the time left is changing, the watchdog is running */
+	if (timeleft1 != timeleft2) {
+		hw->running = true;
+		timeleft = bcm63xx_wdt_get_timeleft(wdd);
+	} else {
+		hw->running = false;
+		timeleft = 0;
+	}
+
 	ret = bcm63xx_timer_register(TIMER_WDT_ID, bcm63xx_wdt_isr, wdd);
 	if (ret < 0) {
 		dev_err(&pdev->dev, "failed to register wdt timer isr\n");
@@ -214,6 +247,8 @@ static int bcm63xx_wdt_probe(struct platform_device *pdev)
 		dev_name(wdd->dev), hw->regs,
 		wdd->timeout, wdd->max_timeout);
 
+	if (timeleft > 0)
+		dev_alert(wdd->dev, "running, reboot in %us\n", timeleft);
 	return 0;
 
 unregister_timer:
@@ -255,6 +290,7 @@ module_platform_driver(bcm63xx_wdt_driver);
 
 MODULE_AUTHOR("Miguel Gaio <miguel.gaio-HH44TBFINEIAvxtiuMwx3w@public.gmane.org>");
 MODULE_AUTHOR("Florian Fainelli <florian-p3rKhJxN3npAfugRpC6u6w@public.gmane.org>");
+MODULE_AUTHOR("Simon Arlott");
 MODULE_DESCRIPTION("Driver for the Broadcom BCM63xx SoC watchdog");
 MODULE_LICENSE("GPL");
 MODULE_ALIAS("platform:bcm63xx-wdt");
-- 
2.1.4

-- 
Simon Arlott
--
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] 95+ messages in thread

* [PATCH 8/10] watchdog: bcm63xx_wdt: Remove dependency on mach-bcm63xx functions/defines
@ 2015-11-22 14:11             ` Simon Arlott
  0 siblings, 0 replies; 95+ messages in thread
From: Simon Arlott @ 2015-11-22 14:11 UTC (permalink / raw)
  To: Guenter Roeck, devicetree, Ralf Baechle, Thomas Gleixner,
	Jason Cooper, Marc Zyngier, Kevin Cernekee, Florian Fainelli,
	Wim Van Sebroeck, Maxime Bizon, Linux Kernel Mailing List,
	linux-mips, linux-watchdog
  Cc: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
	Jonas Gorski

The bcm_readl() and bcm_writel() functions are only defined for
mach-bcm63xx, replace them with __raw_readl() and raw_writel().

The register defines required for the watchdog are in a mach-bcm63xx
header file. Move them to include/linux/bcm63xx_wdt.h so that they are
also available on other machine types.

Signed-off-by: Simon Arlott <simon@fire.lp0.eu>
---
 MAINTAINERS                                       |  1 +
 arch/mips/bcm63xx/prom.c                          |  1 +
 arch/mips/bcm63xx/setup.c                         |  1 +
 arch/mips/include/asm/mach-bcm63xx/bcm63xx_regs.h | 22 --------------------
 drivers/watchdog/bcm63xx_wdt.c                    | 25 +++++++++++------------
 include/linux/bcm63xx_wdt.h                       | 22 ++++++++++++++++++++
 6 files changed, 37 insertions(+), 35 deletions(-)
 create mode 100644 include/linux/bcm63xx_wdt.h

diff --git a/MAINTAINERS b/MAINTAINERS
index 63bf54a..a27b99f 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -2376,6 +2376,7 @@ F:	arch/mips/boot/dts/brcm/bcm*.dts*
 F:	drivers/irqchip/irq-bcm63*
 F:	drivers/irqchip/irq-bcm7*
 F:	drivers/irqchip/irq-brcmstb*
+F:	include/linux/bcm63xx_wdt.h
 
 BROADCOM TG3 GIGABIT ETHERNET DRIVER
 M:	Prashant Sreedharan <prashant@broadcom.com>
diff --git a/arch/mips/bcm63xx/prom.c b/arch/mips/bcm63xx/prom.c
index 7019e29..d2800fb 100644
--- a/arch/mips/bcm63xx/prom.c
+++ b/arch/mips/bcm63xx/prom.c
@@ -6,6 +6,7 @@
  * Copyright (C) 2008 Maxime Bizon <mbizon@freebox.fr>
  */
 
+#include <linux/bcm63xx_wdt.h>
 #include <linux/init.h>
 #include <linux/bootmem.h>
 #include <linux/smp.h>
diff --git a/arch/mips/bcm63xx/setup.c b/arch/mips/bcm63xx/setup.c
index 240fb4f..d1d666d 100644
--- a/arch/mips/bcm63xx/setup.c
+++ b/arch/mips/bcm63xx/setup.c
@@ -6,6 +6,7 @@
  * Copyright (C) 2008 Maxime Bizon <mbizon@freebox.fr>
  */
 
+#include <linux/bcm63xx_wdt.h>
 #include <linux/init.h>
 #include <linux/kernel.h>
 #include <linux/delay.h>
diff --git a/arch/mips/include/asm/mach-bcm63xx/bcm63xx_regs.h b/arch/mips/include/asm/mach-bcm63xx/bcm63xx_regs.h
index 5035f09..16a745b 100644
--- a/arch/mips/include/asm/mach-bcm63xx/bcm63xx_regs.h
+++ b/arch/mips/include/asm/mach-bcm63xx/bcm63xx_regs.h
@@ -441,28 +441,6 @@
 
 
 /*************************************************************************
- * _REG relative to RSET_WDT
- *************************************************************************/
-
-/* Watchdog default count register */
-#define WDT_DEFVAL_REG			0x0
-
-/* Watchdog control register */
-#define WDT_CTL_REG			0x4
-
-/* Watchdog control register constants */
-#define WDT_START_1			(0xff00)
-#define WDT_START_2			(0x00ff)
-#define WDT_STOP_1			(0xee00)
-#define WDT_STOP_2			(0x00ee)
-
-/* Watchdog reset length register */
-#define WDT_RSTLEN_REG			0x8
-
-/* Watchdog soft reset register (BCM6328 only) */
-#define WDT_SOFTRESET_REG		0xc
-
-/*************************************************************************
  * _REG relative to RSET_GPIO
  *************************************************************************/
 
diff --git a/drivers/watchdog/bcm63xx_wdt.c b/drivers/watchdog/bcm63xx_wdt.c
index 7109eb4..7522624 100644
--- a/drivers/watchdog/bcm63xx_wdt.c
+++ b/drivers/watchdog/bcm63xx_wdt.c
@@ -13,6 +13,7 @@
 
 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
 
+#include <linux/bcm63xx_wdt.h>
 #include <linux/clk.h>
 #include <linux/delay.h>
 #include <linux/errno.h>
@@ -27,8 +28,6 @@
 #include <linux/resource.h>
 #include <linux/platform_device.h>
 
-#include <bcm63xx_cpu.h>
-#include <bcm63xx_io.h>
 #include <bcm63xx_regs.h>
 #include <bcm63xx_timer.h>
 
@@ -55,9 +54,9 @@ static int bcm63xx_wdt_start(struct watchdog_device *wdd)
 	unsigned long flags;
 
 	raw_spin_lock_irqsave(&hw->lock, flags);
-	bcm_writel(wdd->timeout * hw->clock_hz, hw->regs + WDT_DEFVAL_REG);
-	bcm_writel(WDT_START_1, hw->regs + WDT_CTL_REG);
-	bcm_writel(WDT_START_2, hw->regs + WDT_CTL_REG);
+	__raw_writel(wdd->timeout * hw->clock_hz, hw->regs + WDT_DEFVAL_REG);
+	__raw_writel(WDT_START_1, hw->regs + WDT_CTL_REG);
+	__raw_writel(WDT_START_2, hw->regs + WDT_CTL_REG);
 	hw->running = true;
 	raw_spin_unlock_irqrestore(&hw->lock, flags);
 	return 0;
@@ -69,8 +68,8 @@ static int bcm63xx_wdt_stop(struct watchdog_device *wdd)
 	unsigned long flags;
 
 	raw_spin_lock_irqsave(&hw->lock, flags);
-	bcm_writel(WDT_STOP_1, hw->regs + WDT_CTL_REG);
-	bcm_writel(WDT_STOP_2, hw->regs + WDT_CTL_REG);
+	__raw_writel(WDT_STOP_1, hw->regs + WDT_CTL_REG);
+	__raw_writel(WDT_STOP_2, hw->regs + WDT_CTL_REG);
 	hw->running = false;
 	raw_spin_unlock_irqrestore(&hw->lock, flags);
 	return 0;
@@ -106,10 +105,10 @@ static void bcm63xx_wdt_isr(void *data)
 	raw_spin_lock_irqsave(&hw->lock, flags);
 	if (!hw->running) {
 		/* Stop the watchdog as it shouldn't be running */
-		bcm_writel(WDT_STOP_1, hw->regs + WDT_CTL_REG);
-		bcm_writel(WDT_STOP_2, hw->regs + WDT_CTL_REG);
+		__raw_writel(WDT_STOP_1, hw->regs + WDT_CTL_REG);
+		__raw_writel(WDT_STOP_2, hw->regs + WDT_CTL_REG);
 	} else {
-		u32 timeleft = bcm_readl(hw->regs + WDT_CTL_REG);
+		u32 timeleft = __raw_readl(hw->regs + WDT_CTL_REG);
 		u32 ms;
 
 		if (timeleft >= 2) {
@@ -123,9 +122,9 @@ static void bcm63xx_wdt_isr(void *data)
 			 * This is done with a lock held in case userspace is
 			 * trying to restart the watchdog on another CPU.
 			 */
-			bcm_writel(timeleft, hw->regs + WDT_DEFVAL_REG);
-			bcm_writel(WDT_START_1, hw->regs + WDT_CTL_REG);
-			bcm_writel(WDT_START_2, hw->regs + WDT_CTL_REG);
+			__raw_writel(timeleft, hw->regs + WDT_DEFVAL_REG);
+			__raw_writel(WDT_START_1, hw->regs + WDT_CTL_REG);
+			__raw_writel(WDT_START_2, hw->regs + WDT_CTL_REG);
 		} else {
 			/* The watchdog cannot be started with a time of less
 			 * than 2 ticks (it won't fire).
diff --git a/include/linux/bcm63xx_wdt.h b/include/linux/bcm63xx_wdt.h
new file mode 100644
index 0000000..2e9210b
--- /dev/null
+++ b/include/linux/bcm63xx_wdt.h
@@ -0,0 +1,22 @@
+#ifndef LINUX_BCM63XX_WDT_H_
+#define LINUX_BCM63XX_WDT_H_
+
+/* Watchdog default count register */
+#define WDT_DEFVAL_REG                  0x0
+
+/* Watchdog control register */
+#define WDT_CTL_REG                     0x4
+
+/* Watchdog control register constants */
+#define WDT_START_1                     (0xff00)
+#define WDT_START_2                     (0x00ff)
+#define WDT_STOP_1                      (0xee00)
+#define WDT_STOP_2                      (0x00ee)
+
+/* Watchdog reset length register */
+#define WDT_RSTLEN_REG                  0x8
+
+/* Watchdog soft reset register (BCM6328 only) */
+#define WDT_SOFTRESET_REG               0xc
+
+#endif
-- 
2.1.4

-- 
Simon Arlott

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

* [PATCH 8/10] watchdog: bcm63xx_wdt: Remove dependency on mach-bcm63xx functions/defines
@ 2015-11-22 14:11             ` Simon Arlott
  0 siblings, 0 replies; 95+ messages in thread
From: Simon Arlott @ 2015-11-22 14:11 UTC (permalink / raw)
  To: Guenter Roeck, devicetree-u79uwXL29TY76Z2rM5mHXA, Ralf Baechle,
	Thomas Gleixner, Jason Cooper, Marc Zyngier, Kevin Cernekee,
	Florian Fainelli, Wim Van Sebroeck, Maxime Bizon,
	Linux Kernel Mailing List, linux-mips-6z/3iImG2C8G8FEW9MqTrA,
	linux-watchdog-u79uwXL29TY76Z2rM5mHXA
  Cc: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
	Jonas Gorski

The bcm_readl() and bcm_writel() functions are only defined for
mach-bcm63xx, replace them with __raw_readl() and raw_writel().

The register defines required for the watchdog are in a mach-bcm63xx
header file. Move them to include/linux/bcm63xx_wdt.h so that they are
also available on other machine types.

Signed-off-by: Simon Arlott <simon-A6De1vDTPLDsq35pWSNszA@public.gmane.org>
---
 MAINTAINERS                                       |  1 +
 arch/mips/bcm63xx/prom.c                          |  1 +
 arch/mips/bcm63xx/setup.c                         |  1 +
 arch/mips/include/asm/mach-bcm63xx/bcm63xx_regs.h | 22 --------------------
 drivers/watchdog/bcm63xx_wdt.c                    | 25 +++++++++++------------
 include/linux/bcm63xx_wdt.h                       | 22 ++++++++++++++++++++
 6 files changed, 37 insertions(+), 35 deletions(-)
 create mode 100644 include/linux/bcm63xx_wdt.h

diff --git a/MAINTAINERS b/MAINTAINERS
index 63bf54a..a27b99f 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -2376,6 +2376,7 @@ F:	arch/mips/boot/dts/brcm/bcm*.dts*
 F:	drivers/irqchip/irq-bcm63*
 F:	drivers/irqchip/irq-bcm7*
 F:	drivers/irqchip/irq-brcmstb*
+F:	include/linux/bcm63xx_wdt.h
 
 BROADCOM TG3 GIGABIT ETHERNET DRIVER
 M:	Prashant Sreedharan <prashant-dY08KVG/lbpWk0Htik3J/w@public.gmane.org>
diff --git a/arch/mips/bcm63xx/prom.c b/arch/mips/bcm63xx/prom.c
index 7019e29..d2800fb 100644
--- a/arch/mips/bcm63xx/prom.c
+++ b/arch/mips/bcm63xx/prom.c
@@ -6,6 +6,7 @@
  * Copyright (C) 2008 Maxime Bizon <mbizon-MmRyKUhfbQ9GWvitb5QawA@public.gmane.org>
  */
 
+#include <linux/bcm63xx_wdt.h>
 #include <linux/init.h>
 #include <linux/bootmem.h>
 #include <linux/smp.h>
diff --git a/arch/mips/bcm63xx/setup.c b/arch/mips/bcm63xx/setup.c
index 240fb4f..d1d666d 100644
--- a/arch/mips/bcm63xx/setup.c
+++ b/arch/mips/bcm63xx/setup.c
@@ -6,6 +6,7 @@
  * Copyright (C) 2008 Maxime Bizon <mbizon-MmRyKUhfbQ9GWvitb5QawA@public.gmane.org>
  */
 
+#include <linux/bcm63xx_wdt.h>
 #include <linux/init.h>
 #include <linux/kernel.h>
 #include <linux/delay.h>
diff --git a/arch/mips/include/asm/mach-bcm63xx/bcm63xx_regs.h b/arch/mips/include/asm/mach-bcm63xx/bcm63xx_regs.h
index 5035f09..16a745b 100644
--- a/arch/mips/include/asm/mach-bcm63xx/bcm63xx_regs.h
+++ b/arch/mips/include/asm/mach-bcm63xx/bcm63xx_regs.h
@@ -441,28 +441,6 @@
 
 
 /*************************************************************************
- * _REG relative to RSET_WDT
- *************************************************************************/
-
-/* Watchdog default count register */
-#define WDT_DEFVAL_REG			0x0
-
-/* Watchdog control register */
-#define WDT_CTL_REG			0x4
-
-/* Watchdog control register constants */
-#define WDT_START_1			(0xff00)
-#define WDT_START_2			(0x00ff)
-#define WDT_STOP_1			(0xee00)
-#define WDT_STOP_2			(0x00ee)
-
-/* Watchdog reset length register */
-#define WDT_RSTLEN_REG			0x8
-
-/* Watchdog soft reset register (BCM6328 only) */
-#define WDT_SOFTRESET_REG		0xc
-
-/*************************************************************************
  * _REG relative to RSET_GPIO
  *************************************************************************/
 
diff --git a/drivers/watchdog/bcm63xx_wdt.c b/drivers/watchdog/bcm63xx_wdt.c
index 7109eb4..7522624 100644
--- a/drivers/watchdog/bcm63xx_wdt.c
+++ b/drivers/watchdog/bcm63xx_wdt.c
@@ -13,6 +13,7 @@
 
 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
 
+#include <linux/bcm63xx_wdt.h>
 #include <linux/clk.h>
 #include <linux/delay.h>
 #include <linux/errno.h>
@@ -27,8 +28,6 @@
 #include <linux/resource.h>
 #include <linux/platform_device.h>
 
-#include <bcm63xx_cpu.h>
-#include <bcm63xx_io.h>
 #include <bcm63xx_regs.h>
 #include <bcm63xx_timer.h>
 
@@ -55,9 +54,9 @@ static int bcm63xx_wdt_start(struct watchdog_device *wdd)
 	unsigned long flags;
 
 	raw_spin_lock_irqsave(&hw->lock, flags);
-	bcm_writel(wdd->timeout * hw->clock_hz, hw->regs + WDT_DEFVAL_REG);
-	bcm_writel(WDT_START_1, hw->regs + WDT_CTL_REG);
-	bcm_writel(WDT_START_2, hw->regs + WDT_CTL_REG);
+	__raw_writel(wdd->timeout * hw->clock_hz, hw->regs + WDT_DEFVAL_REG);
+	__raw_writel(WDT_START_1, hw->regs + WDT_CTL_REG);
+	__raw_writel(WDT_START_2, hw->regs + WDT_CTL_REG);
 	hw->running = true;
 	raw_spin_unlock_irqrestore(&hw->lock, flags);
 	return 0;
@@ -69,8 +68,8 @@ static int bcm63xx_wdt_stop(struct watchdog_device *wdd)
 	unsigned long flags;
 
 	raw_spin_lock_irqsave(&hw->lock, flags);
-	bcm_writel(WDT_STOP_1, hw->regs + WDT_CTL_REG);
-	bcm_writel(WDT_STOP_2, hw->regs + WDT_CTL_REG);
+	__raw_writel(WDT_STOP_1, hw->regs + WDT_CTL_REG);
+	__raw_writel(WDT_STOP_2, hw->regs + WDT_CTL_REG);
 	hw->running = false;
 	raw_spin_unlock_irqrestore(&hw->lock, flags);
 	return 0;
@@ -106,10 +105,10 @@ static void bcm63xx_wdt_isr(void *data)
 	raw_spin_lock_irqsave(&hw->lock, flags);
 	if (!hw->running) {
 		/* Stop the watchdog as it shouldn't be running */
-		bcm_writel(WDT_STOP_1, hw->regs + WDT_CTL_REG);
-		bcm_writel(WDT_STOP_2, hw->regs + WDT_CTL_REG);
+		__raw_writel(WDT_STOP_1, hw->regs + WDT_CTL_REG);
+		__raw_writel(WDT_STOP_2, hw->regs + WDT_CTL_REG);
 	} else {
-		u32 timeleft = bcm_readl(hw->regs + WDT_CTL_REG);
+		u32 timeleft = __raw_readl(hw->regs + WDT_CTL_REG);
 		u32 ms;
 
 		if (timeleft >= 2) {
@@ -123,9 +122,9 @@ static void bcm63xx_wdt_isr(void *data)
 			 * This is done with a lock held in case userspace is
 			 * trying to restart the watchdog on another CPU.
 			 */
-			bcm_writel(timeleft, hw->regs + WDT_DEFVAL_REG);
-			bcm_writel(WDT_START_1, hw->regs + WDT_CTL_REG);
-			bcm_writel(WDT_START_2, hw->regs + WDT_CTL_REG);
+			__raw_writel(timeleft, hw->regs + WDT_DEFVAL_REG);
+			__raw_writel(WDT_START_1, hw->regs + WDT_CTL_REG);
+			__raw_writel(WDT_START_2, hw->regs + WDT_CTL_REG);
 		} else {
 			/* The watchdog cannot be started with a time of less
 			 * than 2 ticks (it won't fire).
diff --git a/include/linux/bcm63xx_wdt.h b/include/linux/bcm63xx_wdt.h
new file mode 100644
index 0000000..2e9210b
--- /dev/null
+++ b/include/linux/bcm63xx_wdt.h
@@ -0,0 +1,22 @@
+#ifndef LINUX_BCM63XX_WDT_H_
+#define LINUX_BCM63XX_WDT_H_
+
+/* Watchdog default count register */
+#define WDT_DEFVAL_REG                  0x0
+
+/* Watchdog control register */
+#define WDT_CTL_REG                     0x4
+
+/* Watchdog control register constants */
+#define WDT_START_1                     (0xff00)
+#define WDT_START_2                     (0x00ff)
+#define WDT_STOP_1                      (0xee00)
+#define WDT_STOP_2                      (0x00ee)
+
+/* Watchdog reset length register */
+#define WDT_RSTLEN_REG                  0x8
+
+/* Watchdog soft reset register (BCM6328 only) */
+#define WDT_SOFTRESET_REG               0xc
+
+#endif
-- 
2.1.4

-- 
Simon Arlott
--
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] 95+ messages in thread

* [PATCH 9/10] watchdog: bcm63xx_wdt: Use bcm63xx_timer interrupt directly
@ 2015-11-22 14:12             ` Simon Arlott
  0 siblings, 0 replies; 95+ messages in thread
From: Simon Arlott @ 2015-11-22 14:12 UTC (permalink / raw)
  To: Guenter Roeck, devicetree, Ralf Baechle, Thomas Gleixner,
	Jason Cooper, Marc Zyngier, Kevin Cernekee, Florian Fainelli,
	Wim Van Sebroeck, Maxime Bizon, Linux Kernel Mailing List,
	linux-mips, linux-watchdog
  Cc: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
	Jonas Gorski

There is only one user of bcm63xx_timer and that is the watchdog.
To allow the watchdog driver to be used on machine types other than
mach-bcm63xx, it needs to use an interrupt instead of a custom register
function.

Modify bcm63xx_timer to only disable the timers (so that they don't
interfere with the watchdog if an interrupt occurs) and remove its
exported functions.

Use the timer interrupt directly in bcm63xx_wdt.

Signed-off-by: Simon Arlott <simon@fire.lp0.eu>
---
 arch/mips/bcm63xx/dev-wdt.c                        |   7 +
 arch/mips/bcm63xx/timer.c                          | 181 +--------------------
 arch/mips/include/asm/mach-bcm63xx/bcm63xx_timer.h |  11 --
 drivers/watchdog/bcm63xx_wdt.c                     |  45 +++--
 4 files changed, 38 insertions(+), 206 deletions(-)
 delete mode 100644 arch/mips/include/asm/mach-bcm63xx/bcm63xx_timer.h

diff --git a/arch/mips/bcm63xx/dev-wdt.c b/arch/mips/bcm63xx/dev-wdt.c
index 2a2346a..a7a5497 100644
--- a/arch/mips/bcm63xx/dev-wdt.c
+++ b/arch/mips/bcm63xx/dev-wdt.c
@@ -17,6 +17,11 @@ static struct resource wdt_resources[] = {
 		.end		= -1, /* filled at runtime */
 		.flags		= IORESOURCE_MEM,
 	},
+	{
+		.start		= -1, /* filled at runtime */
+		.end		= -1, /* filled at runtime */
+		.flags		= IORESOURCE_IRQ,
+	},
 };
 
 static struct platform_device bcm63xx_wdt_device = {
@@ -32,6 +37,8 @@ int __init bcm63xx_wdt_register(void)
 	wdt_resources[0].end = wdt_resources[0].start;
 	wdt_resources[0].end += RSET_WDT_SIZE - 1;
 
+	wdt_resources[1].start = bcm63xx_get_irq_number(IRQ_TIMER);
+
 	return platform_device_register(&bcm63xx_wdt_device);
 }
 arch_initcall(bcm63xx_wdt_register);
diff --git a/arch/mips/bcm63xx/timer.c b/arch/mips/bcm63xx/timer.c
index 5f11359..9c7b41a6 100644
--- a/arch/mips/bcm63xx/timer.c
+++ b/arch/mips/bcm63xx/timer.c
@@ -9,196 +9,23 @@
 #include <linux/kernel.h>
 #include <linux/err.h>
 #include <linux/module.h>
-#include <linux/spinlock.h>
-#include <linux/interrupt.h>
-#include <linux/clk.h>
 #include <bcm63xx_cpu.h>
 #include <bcm63xx_io.h>
-#include <bcm63xx_timer.h>
 #include <bcm63xx_regs.h>
 
-static DEFINE_RAW_SPINLOCK(timer_reg_lock);
-static DEFINE_RAW_SPINLOCK(timer_data_lock);
-static struct clk *periph_clk;
-
-static struct timer_data {
-	void	(*cb)(void *);
-	void	*data;
-} timer_data[BCM63XX_TIMER_COUNT];
-
-static irqreturn_t timer_interrupt(int irq, void *dev_id)
-{
-	u32 stat;
-	int i;
-
-	raw_spin_lock(&timer_reg_lock);
-	stat = bcm_timer_readl(TIMER_IRQSTAT_REG);
-	bcm_timer_writel(stat, TIMER_IRQSTAT_REG);
-	raw_spin_unlock(&timer_reg_lock);
-
-	for (i = 0; i < BCM63XX_TIMER_COUNT; i++) {
-		if (!(stat & TIMER_IRQSTAT_TIMER_CAUSE(i)))
-			continue;
-
-		raw_spin_lock(&timer_data_lock);
-		if (!timer_data[i].cb) {
-			raw_spin_unlock(&timer_data_lock);
-			continue;
-		}
-
-		timer_data[i].cb(timer_data[i].data);
-		raw_spin_unlock(&timer_data_lock);
-	}
-
-	return IRQ_HANDLED;
-}
-
-int bcm63xx_timer_enable(int id)
-{
-	u32 reg;
-	unsigned long flags;
-
-	if (id >= BCM63XX_TIMER_COUNT)
-		return -EINVAL;
-
-	raw_spin_lock_irqsave(&timer_reg_lock, flags);
-
-	reg = bcm_timer_readl(TIMER_CTLx_REG(id));
-	reg |= TIMER_CTL_ENABLE_MASK;
-	bcm_timer_writel(reg, TIMER_CTLx_REG(id));
-
-	reg = bcm_timer_readl(TIMER_IRQSTAT_REG);
-	reg |= TIMER_IRQSTAT_TIMER_IR_EN(id);
-	bcm_timer_writel(reg, TIMER_IRQSTAT_REG);
-
-	raw_spin_unlock_irqrestore(&timer_reg_lock, flags);
-	return 0;
-}
-
-EXPORT_SYMBOL(bcm63xx_timer_enable);
-
-int bcm63xx_timer_disable(int id)
+static int bcm63xx_timer_init(void)
 {
 	u32 reg;
-	unsigned long flags;
-
-	if (id >= BCM63XX_TIMER_COUNT)
-		return -EINVAL;
-
-	raw_spin_lock_irqsave(&timer_reg_lock, flags);
-
-	reg = bcm_timer_readl(TIMER_CTLx_REG(id));
-	reg &= ~TIMER_CTL_ENABLE_MASK;
-	bcm_timer_writel(reg, TIMER_CTLx_REG(id));
-
-	reg = bcm_timer_readl(TIMER_IRQSTAT_REG);
-	reg &= ~TIMER_IRQSTAT_TIMER_IR_EN(id);
-	bcm_timer_writel(reg, TIMER_IRQSTAT_REG);
-
-	raw_spin_unlock_irqrestore(&timer_reg_lock, flags);
-	return 0;
-}
-
-EXPORT_SYMBOL(bcm63xx_timer_disable);
-
-int bcm63xx_timer_register(int id, void (*callback)(void *data), void *data)
-{
-	unsigned long flags;
-	int ret;
-
-	if (id >= BCM63XX_TIMER_COUNT || !callback)
-		return -EINVAL;
-
-	ret = 0;
-	raw_spin_lock_irqsave(&timer_data_lock, flags);
-	if (timer_data[id].cb) {
-		ret = -EBUSY;
-		goto out;
-	}
-
-	timer_data[id].cb = callback;
-	timer_data[id].data = data;
-
-out:
-	raw_spin_unlock_irqrestore(&timer_data_lock, flags);
-	return ret;
-}
-
-EXPORT_SYMBOL(bcm63xx_timer_register);
-
-void bcm63xx_timer_unregister(int id)
-{
-	unsigned long flags;
-
-	if (id >= BCM63XX_TIMER_COUNT)
-		return;
-
-	raw_spin_lock_irqsave(&timer_data_lock, flags);
-	timer_data[id].cb = NULL;
-	raw_spin_unlock_irqrestore(&timer_data_lock, flags);
-}
-
-EXPORT_SYMBOL(bcm63xx_timer_unregister);
-
-unsigned int bcm63xx_timer_countdown(unsigned int countdown_us)
-{
-	return (clk_get_rate(periph_clk) / (1000 * 1000)) * countdown_us;
-}
-
-EXPORT_SYMBOL(bcm63xx_timer_countdown);
-
-int bcm63xx_timer_set(int id, int monotonic, unsigned int countdown_us)
-{
-	u32 reg, countdown;
-	unsigned long flags;
-
-	if (id >= BCM63XX_TIMER_COUNT)
-		return -EINVAL;
-
-	countdown = bcm63xx_timer_countdown(countdown_us);
-	if (countdown & ~TIMER_CTL_COUNTDOWN_MASK)
-		return -EINVAL;
-
-	raw_spin_lock_irqsave(&timer_reg_lock, flags);
-	reg = bcm_timer_readl(TIMER_CTLx_REG(id));
-
-	if (monotonic)
-		reg &= ~TIMER_CTL_MONOTONIC_MASK;
-	else
-		reg |= TIMER_CTL_MONOTONIC_MASK;
-
-	reg &= ~TIMER_CTL_COUNTDOWN_MASK;
-	reg |= countdown;
-	bcm_timer_writel(reg, TIMER_CTLx_REG(id));
-
-	raw_spin_unlock_irqrestore(&timer_reg_lock, flags);
-	return 0;
-}
-
-EXPORT_SYMBOL(bcm63xx_timer_set);
-
-int bcm63xx_timer_init(void)
-{
-	int ret, irq;
-	u32 reg;
 
+	/* Disable all timers so that they won't interfere with use of the
+	 * timer interrupt by the watchdog.
+	 */
 	reg = bcm_timer_readl(TIMER_IRQSTAT_REG);
 	reg &= ~TIMER_IRQSTAT_TIMER0_IR_EN;
 	reg &= ~TIMER_IRQSTAT_TIMER1_IR_EN;
 	reg &= ~TIMER_IRQSTAT_TIMER2_IR_EN;
 	bcm_timer_writel(reg, TIMER_IRQSTAT_REG);
 
-	periph_clk = clk_get(NULL, "periph");
-	if (IS_ERR(periph_clk))
-		return -ENODEV;
-
-	irq = bcm63xx_get_irq_number(IRQ_TIMER);
-	ret = request_irq(irq, timer_interrupt, 0, "bcm63xx_timer", NULL);
-	if (ret) {
-		printk(KERN_ERR "bcm63xx_timer: failed to register irq\n");
-		return ret;
-	}
-
 	return 0;
 }
 
diff --git a/arch/mips/include/asm/mach-bcm63xx/bcm63xx_timer.h b/arch/mips/include/asm/mach-bcm63xx/bcm63xx_timer.h
deleted file mode 100644
index c0fce83..0000000
--- a/arch/mips/include/asm/mach-bcm63xx/bcm63xx_timer.h
+++ /dev/null
@@ -1,11 +0,0 @@
-#ifndef BCM63XX_TIMER_H_
-#define BCM63XX_TIMER_H_
-
-int bcm63xx_timer_register(int id, void (*callback)(void *data), void *data);
-void bcm63xx_timer_unregister(int id);
-int bcm63xx_timer_set(int id, int monotonic, unsigned int countdown_us);
-int bcm63xx_timer_enable(int id);
-int bcm63xx_timer_disable(int id);
-unsigned int bcm63xx_timer_countdown(unsigned int countdown_us);
-
-#endif /* !BCM63XX_TIMER_H_ */
diff --git a/drivers/watchdog/bcm63xx_wdt.c b/drivers/watchdog/bcm63xx_wdt.c
index 7522624..9989efe 100644
--- a/drivers/watchdog/bcm63xx_wdt.c
+++ b/drivers/watchdog/bcm63xx_wdt.c
@@ -17,6 +17,7 @@
 #include <linux/clk.h>
 #include <linux/delay.h>
 #include <linux/errno.h>
+#include <linux/interrupt.h>
 #include <linux/io.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
@@ -28,9 +29,6 @@
 #include <linux/resource.h>
 #include <linux/platform_device.h>
 
-#include <bcm63xx_regs.h>
-#include <bcm63xx_timer.h>
-
 #define PFX KBUILD_MODNAME
 
 #define WDT_CLK_NAME		"periph"
@@ -40,6 +38,7 @@ struct bcm63xx_wdt_hw {
 	void __iomem *regs;
 	struct clk *clk;
 	unsigned long clock_hz;
+	int irq;
 	bool running;
 };
 
@@ -96,7 +95,7 @@ static int bcm63xx_wdt_set_timeout(struct watchdog_device *wdd,
 }
 
 /* The watchdog interrupt occurs when half the timeout is remaining */
-static void bcm63xx_wdt_isr(void *data)
+static irqreturn_t bcm63xx_wdt_interrupt(int irq, void *data)
 {
 	struct watchdog_device *wdd = data;
 	struct bcm63xx_wdt_hw *hw = watchdog_get_drvdata(wdd);
@@ -137,6 +136,7 @@ static void bcm63xx_wdt_isr(void *data)
 			"warning timer fired, reboot in %ums\n", ms);
 	}
 	raw_spin_unlock_irqrestore(&hw->lock, flags);
+	return IRQ_HANDLED;
 }
 
 static struct watchdog_ops bcm63xx_wdt_ops = {
@@ -229,30 +229,37 @@ static int bcm63xx_wdt_probe(struct platform_device *pdev)
 		timeleft = 0;
 	}
 
-	ret = bcm63xx_timer_register(TIMER_WDT_ID, bcm63xx_wdt_isr, wdd);
+	ret = watchdog_register_device(wdd);
 	if (ret < 0) {
-		dev_err(&pdev->dev, "failed to register wdt timer isr\n");
+		dev_err(&pdev->dev, "failed to register watchdog device\n");
 		goto disable_clk;
 	}
 
-	ret = watchdog_register_device(wdd);
-	if (ret < 0) {
-		dev_err(&pdev->dev, "failed to register watchdog device\n");
-		goto unregister_timer;
+	hw->irq = platform_get_irq(pdev, 0);
+	if (hw->irq >= 0) {
+		ret = devm_request_irq(&pdev->dev, hw->irq,
+			bcm63xx_wdt_interrupt, IRQF_TIMER,
+			dev_name(&pdev->dev), wdd);
+		if (ret)
+			hw->irq = -1;
 	}
 
-	dev_info(&pdev->dev,
-		"%s at MMIO 0x%p (timeout = %us, max_timeout = %us)",
-		dev_name(wdd->dev), hw->regs,
-		wdd->timeout, wdd->max_timeout);
+	if (hw->irq >= 0) {
+		dev_info(&pdev->dev,
+			"%s at MMIO 0x%p (irq = %d, timeout = %us, max_timeout = %us)",
+			dev_name(wdd->dev), hw->regs, hw->irq,
+			wdd->timeout, wdd->max_timeout);
+	} else {
+		dev_info(&pdev->dev,
+			"%s at MMIO 0x%p (timeout = %us, max_timeout = %us)",
+			dev_name(wdd->dev), hw->regs,
+			wdd->timeout, wdd->max_timeout);
+	}
 
 	if (timeleft > 0)
 		dev_alert(wdd->dev, "running, reboot in %us\n", timeleft);
 	return 0;
 
-unregister_timer:
-	bcm63xx_timer_unregister(TIMER_WDT_ID);
-
 disable_clk:
 	clk_disable_unprepare(hw->clk);
 	return ret;
@@ -263,7 +270,9 @@ static int bcm63xx_wdt_remove(struct platform_device *pdev)
 	struct watchdog_device *wdd = platform_get_drvdata(pdev);
 	struct bcm63xx_wdt_hw *hw = watchdog_get_drvdata(wdd);
 
-	bcm63xx_timer_unregister(TIMER_WDT_ID);
+	if (hw->irq >= 0)
+		devm_free_irq(&pdev->dev, hw->irq, wdd);
+
 	watchdog_unregister_device(wdd);
 	clk_disable_unprepare(hw->clk);
 	return 0;
-- 
2.1.4

-- 
Simon Arlott

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

* [PATCH 9/10] watchdog: bcm63xx_wdt: Use bcm63xx_timer interrupt directly
@ 2015-11-22 14:12             ` Simon Arlott
  0 siblings, 0 replies; 95+ messages in thread
From: Simon Arlott @ 2015-11-22 14:12 UTC (permalink / raw)
  To: Guenter Roeck, devicetree-u79uwXL29TY76Z2rM5mHXA, Ralf Baechle,
	Thomas Gleixner, Jason Cooper, Marc Zyngier, Kevin Cernekee,
	Florian Fainelli, Wim Van Sebroeck, Maxime Bizon,
	Linux Kernel Mailing List, linux-mips-6z/3iImG2C8G8FEW9MqTrA,
	linux-watchdog-u79uwXL29TY76Z2rM5mHXA
  Cc: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
	Jonas Gorski

There is only one user of bcm63xx_timer and that is the watchdog.
To allow the watchdog driver to be used on machine types other than
mach-bcm63xx, it needs to use an interrupt instead of a custom register
function.

Modify bcm63xx_timer to only disable the timers (so that they don't
interfere with the watchdog if an interrupt occurs) and remove its
exported functions.

Use the timer interrupt directly in bcm63xx_wdt.

Signed-off-by: Simon Arlott <simon-A6De1vDTPLDsq35pWSNszA@public.gmane.org>
---
 arch/mips/bcm63xx/dev-wdt.c                        |   7 +
 arch/mips/bcm63xx/timer.c                          | 181 +--------------------
 arch/mips/include/asm/mach-bcm63xx/bcm63xx_timer.h |  11 --
 drivers/watchdog/bcm63xx_wdt.c                     |  45 +++--
 4 files changed, 38 insertions(+), 206 deletions(-)
 delete mode 100644 arch/mips/include/asm/mach-bcm63xx/bcm63xx_timer.h

diff --git a/arch/mips/bcm63xx/dev-wdt.c b/arch/mips/bcm63xx/dev-wdt.c
index 2a2346a..a7a5497 100644
--- a/arch/mips/bcm63xx/dev-wdt.c
+++ b/arch/mips/bcm63xx/dev-wdt.c
@@ -17,6 +17,11 @@ static struct resource wdt_resources[] = {
 		.end		= -1, /* filled at runtime */
 		.flags		= IORESOURCE_MEM,
 	},
+	{
+		.start		= -1, /* filled at runtime */
+		.end		= -1, /* filled at runtime */
+		.flags		= IORESOURCE_IRQ,
+	},
 };
 
 static struct platform_device bcm63xx_wdt_device = {
@@ -32,6 +37,8 @@ int __init bcm63xx_wdt_register(void)
 	wdt_resources[0].end = wdt_resources[0].start;
 	wdt_resources[0].end += RSET_WDT_SIZE - 1;
 
+	wdt_resources[1].start = bcm63xx_get_irq_number(IRQ_TIMER);
+
 	return platform_device_register(&bcm63xx_wdt_device);
 }
 arch_initcall(bcm63xx_wdt_register);
diff --git a/arch/mips/bcm63xx/timer.c b/arch/mips/bcm63xx/timer.c
index 5f11359..9c7b41a6 100644
--- a/arch/mips/bcm63xx/timer.c
+++ b/arch/mips/bcm63xx/timer.c
@@ -9,196 +9,23 @@
 #include <linux/kernel.h>
 #include <linux/err.h>
 #include <linux/module.h>
-#include <linux/spinlock.h>
-#include <linux/interrupt.h>
-#include <linux/clk.h>
 #include <bcm63xx_cpu.h>
 #include <bcm63xx_io.h>
-#include <bcm63xx_timer.h>
 #include <bcm63xx_regs.h>
 
-static DEFINE_RAW_SPINLOCK(timer_reg_lock);
-static DEFINE_RAW_SPINLOCK(timer_data_lock);
-static struct clk *periph_clk;
-
-static struct timer_data {
-	void	(*cb)(void *);
-	void	*data;
-} timer_data[BCM63XX_TIMER_COUNT];
-
-static irqreturn_t timer_interrupt(int irq, void *dev_id)
-{
-	u32 stat;
-	int i;
-
-	raw_spin_lock(&timer_reg_lock);
-	stat = bcm_timer_readl(TIMER_IRQSTAT_REG);
-	bcm_timer_writel(stat, TIMER_IRQSTAT_REG);
-	raw_spin_unlock(&timer_reg_lock);
-
-	for (i = 0; i < BCM63XX_TIMER_COUNT; i++) {
-		if (!(stat & TIMER_IRQSTAT_TIMER_CAUSE(i)))
-			continue;
-
-		raw_spin_lock(&timer_data_lock);
-		if (!timer_data[i].cb) {
-			raw_spin_unlock(&timer_data_lock);
-			continue;
-		}
-
-		timer_data[i].cb(timer_data[i].data);
-		raw_spin_unlock(&timer_data_lock);
-	}
-
-	return IRQ_HANDLED;
-}
-
-int bcm63xx_timer_enable(int id)
-{
-	u32 reg;
-	unsigned long flags;
-
-	if (id >= BCM63XX_TIMER_COUNT)
-		return -EINVAL;
-
-	raw_spin_lock_irqsave(&timer_reg_lock, flags);
-
-	reg = bcm_timer_readl(TIMER_CTLx_REG(id));
-	reg |= TIMER_CTL_ENABLE_MASK;
-	bcm_timer_writel(reg, TIMER_CTLx_REG(id));
-
-	reg = bcm_timer_readl(TIMER_IRQSTAT_REG);
-	reg |= TIMER_IRQSTAT_TIMER_IR_EN(id);
-	bcm_timer_writel(reg, TIMER_IRQSTAT_REG);
-
-	raw_spin_unlock_irqrestore(&timer_reg_lock, flags);
-	return 0;
-}
-
-EXPORT_SYMBOL(bcm63xx_timer_enable);
-
-int bcm63xx_timer_disable(int id)
+static int bcm63xx_timer_init(void)
 {
 	u32 reg;
-	unsigned long flags;
-
-	if (id >= BCM63XX_TIMER_COUNT)
-		return -EINVAL;
-
-	raw_spin_lock_irqsave(&timer_reg_lock, flags);
-
-	reg = bcm_timer_readl(TIMER_CTLx_REG(id));
-	reg &= ~TIMER_CTL_ENABLE_MASK;
-	bcm_timer_writel(reg, TIMER_CTLx_REG(id));
-
-	reg = bcm_timer_readl(TIMER_IRQSTAT_REG);
-	reg &= ~TIMER_IRQSTAT_TIMER_IR_EN(id);
-	bcm_timer_writel(reg, TIMER_IRQSTAT_REG);
-
-	raw_spin_unlock_irqrestore(&timer_reg_lock, flags);
-	return 0;
-}
-
-EXPORT_SYMBOL(bcm63xx_timer_disable);
-
-int bcm63xx_timer_register(int id, void (*callback)(void *data), void *data)
-{
-	unsigned long flags;
-	int ret;
-
-	if (id >= BCM63XX_TIMER_COUNT || !callback)
-		return -EINVAL;
-
-	ret = 0;
-	raw_spin_lock_irqsave(&timer_data_lock, flags);
-	if (timer_data[id].cb) {
-		ret = -EBUSY;
-		goto out;
-	}
-
-	timer_data[id].cb = callback;
-	timer_data[id].data = data;
-
-out:
-	raw_spin_unlock_irqrestore(&timer_data_lock, flags);
-	return ret;
-}
-
-EXPORT_SYMBOL(bcm63xx_timer_register);
-
-void bcm63xx_timer_unregister(int id)
-{
-	unsigned long flags;
-
-	if (id >= BCM63XX_TIMER_COUNT)
-		return;
-
-	raw_spin_lock_irqsave(&timer_data_lock, flags);
-	timer_data[id].cb = NULL;
-	raw_spin_unlock_irqrestore(&timer_data_lock, flags);
-}
-
-EXPORT_SYMBOL(bcm63xx_timer_unregister);
-
-unsigned int bcm63xx_timer_countdown(unsigned int countdown_us)
-{
-	return (clk_get_rate(periph_clk) / (1000 * 1000)) * countdown_us;
-}
-
-EXPORT_SYMBOL(bcm63xx_timer_countdown);
-
-int bcm63xx_timer_set(int id, int monotonic, unsigned int countdown_us)
-{
-	u32 reg, countdown;
-	unsigned long flags;
-
-	if (id >= BCM63XX_TIMER_COUNT)
-		return -EINVAL;
-
-	countdown = bcm63xx_timer_countdown(countdown_us);
-	if (countdown & ~TIMER_CTL_COUNTDOWN_MASK)
-		return -EINVAL;
-
-	raw_spin_lock_irqsave(&timer_reg_lock, flags);
-	reg = bcm_timer_readl(TIMER_CTLx_REG(id));
-
-	if (monotonic)
-		reg &= ~TIMER_CTL_MONOTONIC_MASK;
-	else
-		reg |= TIMER_CTL_MONOTONIC_MASK;
-
-	reg &= ~TIMER_CTL_COUNTDOWN_MASK;
-	reg |= countdown;
-	bcm_timer_writel(reg, TIMER_CTLx_REG(id));
-
-	raw_spin_unlock_irqrestore(&timer_reg_lock, flags);
-	return 0;
-}
-
-EXPORT_SYMBOL(bcm63xx_timer_set);
-
-int bcm63xx_timer_init(void)
-{
-	int ret, irq;
-	u32 reg;
 
+	/* Disable all timers so that they won't interfere with use of the
+	 * timer interrupt by the watchdog.
+	 */
 	reg = bcm_timer_readl(TIMER_IRQSTAT_REG);
 	reg &= ~TIMER_IRQSTAT_TIMER0_IR_EN;
 	reg &= ~TIMER_IRQSTAT_TIMER1_IR_EN;
 	reg &= ~TIMER_IRQSTAT_TIMER2_IR_EN;
 	bcm_timer_writel(reg, TIMER_IRQSTAT_REG);
 
-	periph_clk = clk_get(NULL, "periph");
-	if (IS_ERR(periph_clk))
-		return -ENODEV;
-
-	irq = bcm63xx_get_irq_number(IRQ_TIMER);
-	ret = request_irq(irq, timer_interrupt, 0, "bcm63xx_timer", NULL);
-	if (ret) {
-		printk(KERN_ERR "bcm63xx_timer: failed to register irq\n");
-		return ret;
-	}
-
 	return 0;
 }
 
diff --git a/arch/mips/include/asm/mach-bcm63xx/bcm63xx_timer.h b/arch/mips/include/asm/mach-bcm63xx/bcm63xx_timer.h
deleted file mode 100644
index c0fce83..0000000
--- a/arch/mips/include/asm/mach-bcm63xx/bcm63xx_timer.h
+++ /dev/null
@@ -1,11 +0,0 @@
-#ifndef BCM63XX_TIMER_H_
-#define BCM63XX_TIMER_H_
-
-int bcm63xx_timer_register(int id, void (*callback)(void *data), void *data);
-void bcm63xx_timer_unregister(int id);
-int bcm63xx_timer_set(int id, int monotonic, unsigned int countdown_us);
-int bcm63xx_timer_enable(int id);
-int bcm63xx_timer_disable(int id);
-unsigned int bcm63xx_timer_countdown(unsigned int countdown_us);
-
-#endif /* !BCM63XX_TIMER_H_ */
diff --git a/drivers/watchdog/bcm63xx_wdt.c b/drivers/watchdog/bcm63xx_wdt.c
index 7522624..9989efe 100644
--- a/drivers/watchdog/bcm63xx_wdt.c
+++ b/drivers/watchdog/bcm63xx_wdt.c
@@ -17,6 +17,7 @@
 #include <linux/clk.h>
 #include <linux/delay.h>
 #include <linux/errno.h>
+#include <linux/interrupt.h>
 #include <linux/io.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
@@ -28,9 +29,6 @@
 #include <linux/resource.h>
 #include <linux/platform_device.h>
 
-#include <bcm63xx_regs.h>
-#include <bcm63xx_timer.h>
-
 #define PFX KBUILD_MODNAME
 
 #define WDT_CLK_NAME		"periph"
@@ -40,6 +38,7 @@ struct bcm63xx_wdt_hw {
 	void __iomem *regs;
 	struct clk *clk;
 	unsigned long clock_hz;
+	int irq;
 	bool running;
 };
 
@@ -96,7 +95,7 @@ static int bcm63xx_wdt_set_timeout(struct watchdog_device *wdd,
 }
 
 /* The watchdog interrupt occurs when half the timeout is remaining */
-static void bcm63xx_wdt_isr(void *data)
+static irqreturn_t bcm63xx_wdt_interrupt(int irq, void *data)
 {
 	struct watchdog_device *wdd = data;
 	struct bcm63xx_wdt_hw *hw = watchdog_get_drvdata(wdd);
@@ -137,6 +136,7 @@ static void bcm63xx_wdt_isr(void *data)
 			"warning timer fired, reboot in %ums\n", ms);
 	}
 	raw_spin_unlock_irqrestore(&hw->lock, flags);
+	return IRQ_HANDLED;
 }
 
 static struct watchdog_ops bcm63xx_wdt_ops = {
@@ -229,30 +229,37 @@ static int bcm63xx_wdt_probe(struct platform_device *pdev)
 		timeleft = 0;
 	}
 
-	ret = bcm63xx_timer_register(TIMER_WDT_ID, bcm63xx_wdt_isr, wdd);
+	ret = watchdog_register_device(wdd);
 	if (ret < 0) {
-		dev_err(&pdev->dev, "failed to register wdt timer isr\n");
+		dev_err(&pdev->dev, "failed to register watchdog device\n");
 		goto disable_clk;
 	}
 
-	ret = watchdog_register_device(wdd);
-	if (ret < 0) {
-		dev_err(&pdev->dev, "failed to register watchdog device\n");
-		goto unregister_timer;
+	hw->irq = platform_get_irq(pdev, 0);
+	if (hw->irq >= 0) {
+		ret = devm_request_irq(&pdev->dev, hw->irq,
+			bcm63xx_wdt_interrupt, IRQF_TIMER,
+			dev_name(&pdev->dev), wdd);
+		if (ret)
+			hw->irq = -1;
 	}
 
-	dev_info(&pdev->dev,
-		"%s at MMIO 0x%p (timeout = %us, max_timeout = %us)",
-		dev_name(wdd->dev), hw->regs,
-		wdd->timeout, wdd->max_timeout);
+	if (hw->irq >= 0) {
+		dev_info(&pdev->dev,
+			"%s at MMIO 0x%p (irq = %d, timeout = %us, max_timeout = %us)",
+			dev_name(wdd->dev), hw->regs, hw->irq,
+			wdd->timeout, wdd->max_timeout);
+	} else {
+		dev_info(&pdev->dev,
+			"%s at MMIO 0x%p (timeout = %us, max_timeout = %us)",
+			dev_name(wdd->dev), hw->regs,
+			wdd->timeout, wdd->max_timeout);
+	}
 
 	if (timeleft > 0)
 		dev_alert(wdd->dev, "running, reboot in %us\n", timeleft);
 	return 0;
 
-unregister_timer:
-	bcm63xx_timer_unregister(TIMER_WDT_ID);
-
 disable_clk:
 	clk_disable_unprepare(hw->clk);
 	return ret;
@@ -263,7 +270,9 @@ static int bcm63xx_wdt_remove(struct platform_device *pdev)
 	struct watchdog_device *wdd = platform_get_drvdata(pdev);
 	struct bcm63xx_wdt_hw *hw = watchdog_get_drvdata(wdd);
 
-	bcm63xx_timer_unregister(TIMER_WDT_ID);
+	if (hw->irq >= 0)
+		devm_free_irq(&pdev->dev, hw->irq, wdd);
+
 	watchdog_unregister_device(wdd);
 	clk_disable_unprepare(hw->clk);
 	return 0;
-- 
2.1.4

-- 
Simon Arlott
--
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] 95+ messages in thread

* [PATCH 10/10] watchdog: bcm63xx_wdt: Use brcm,bcm6345-wdt device tree binding
@ 2015-11-22 14:14             ` Simon Arlott
  0 siblings, 0 replies; 95+ messages in thread
From: Simon Arlott @ 2015-11-22 14:14 UTC (permalink / raw)
  To: Guenter Roeck, devicetree, Ralf Baechle, Thomas Gleixner,
	Jason Cooper, Marc Zyngier, Kevin Cernekee, Florian Fainelli,
	Wim Van Sebroeck, Maxime Bizon, Linux Kernel Mailing List,
	linux-mips, linux-watchdog
  Cc: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
	Jonas Gorski

Add of_match_table for "brcm,bcm6345-wdt".

Use a NULL clock name when not on mach-bcm63xx so that the device tree
clock name does not have to be "periph".

Allow the watchdog to be selected on BMIPS_GENERIC and select the BCM6345
timer interrupt handler.

Signed-off-by: Simon Arlott <simon@fire.lp0.eu>
---
 drivers/watchdog/Kconfig       |  3 ++-
 drivers/watchdog/bcm63xx_wdt.c | 14 +++++++++++++-
 2 files changed, 15 insertions(+), 2 deletions(-)

diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index 6815b74..0c50add 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -1272,8 +1272,9 @@ config OCTEON_WDT
 
 config BCM63XX_WDT
 	tristate "Broadcom BCM63xx hardware watchdog"
-	depends on BCM63XX
+	depends on BCM63XX || BMIPS_GENERIC
 	select WATCHDOG_CORE
+	select BCM6345_L2_TIMER_IRQ if BMIPS_GENERIC
 	help
 	  Watchdog driver for the built in watchdog hardware in Broadcom
 	  BCM63xx SoC.
diff --git a/drivers/watchdog/bcm63xx_wdt.c b/drivers/watchdog/bcm63xx_wdt.c
index 9989efe..51b855e 100644
--- a/drivers/watchdog/bcm63xx_wdt.c
+++ b/drivers/watchdog/bcm63xx_wdt.c
@@ -22,6 +22,7 @@
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/moduleparam.h>
+#include <linux/mod_devicetable.h>
 #include <linux/spinlock.h>
 #include <linux/types.h>
 #include <linux/watchdog.h>
@@ -31,7 +32,11 @@
 
 #define PFX KBUILD_MODNAME
 
-#define WDT_CLK_NAME		"periph"
+#ifdef CONFIG_BCM63XX
+# define WDT_CLK_NAME		"periph"
+#else
+# define WDT_CLK_NAME		NULL
+#endif
 
 struct bcm63xx_wdt_hw {
 	raw_spinlock_t lock;
@@ -285,12 +290,19 @@ static void bcm63xx_wdt_shutdown(struct platform_device *pdev)
 	bcm63xx_wdt_stop(wdd);
 }
 
+static const struct of_device_id bcm63xx_wdt_dt_ids[] = {
+	{ .compatible = "brcm,bcm6345-wdt" },
+	{}
+};
+MODULE_DEVICE_TABLE(of, bcm63xx_wdt_dt_ids);
+
 static struct platform_driver bcm63xx_wdt_driver = {
 	.probe	= bcm63xx_wdt_probe,
 	.remove = bcm63xx_wdt_remove,
 	.shutdown = bcm63xx_wdt_shutdown,
 	.driver = {
 		.name = "bcm63xx-wdt",
+		.of_match_table = bcm63xx_wdt_dt_ids,
 	}
 };
 
-- 
2.1.4

-- 
Simon Arlott

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

* [PATCH 10/10] watchdog: bcm63xx_wdt: Use brcm,bcm6345-wdt device tree binding
@ 2015-11-22 14:14             ` Simon Arlott
  0 siblings, 0 replies; 95+ messages in thread
From: Simon Arlott @ 2015-11-22 14:14 UTC (permalink / raw)
  To: Guenter Roeck, devicetree-u79uwXL29TY76Z2rM5mHXA, Ralf Baechle,
	Thomas Gleixner, Jason Cooper, Marc Zyngier, Kevin Cernekee,
	Florian Fainelli, Wim Van Sebroeck, Maxime Bizon,
	Linux Kernel Mailing List, linux-mips-6z/3iImG2C8G8FEW9MqTrA,
	linux-watchdog-u79uwXL29TY76Z2rM5mHXA
  Cc: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
	Jonas Gorski

Add of_match_table for "brcm,bcm6345-wdt".

Use a NULL clock name when not on mach-bcm63xx so that the device tree
clock name does not have to be "periph".

Allow the watchdog to be selected on BMIPS_GENERIC and select the BCM6345
timer interrupt handler.

Signed-off-by: Simon Arlott <simon-A6De1vDTPLDsq35pWSNszA@public.gmane.org>
---
 drivers/watchdog/Kconfig       |  3 ++-
 drivers/watchdog/bcm63xx_wdt.c | 14 +++++++++++++-
 2 files changed, 15 insertions(+), 2 deletions(-)

diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index 6815b74..0c50add 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -1272,8 +1272,9 @@ config OCTEON_WDT
 
 config BCM63XX_WDT
 	tristate "Broadcom BCM63xx hardware watchdog"
-	depends on BCM63XX
+	depends on BCM63XX || BMIPS_GENERIC
 	select WATCHDOG_CORE
+	select BCM6345_L2_TIMER_IRQ if BMIPS_GENERIC
 	help
 	  Watchdog driver for the built in watchdog hardware in Broadcom
 	  BCM63xx SoC.
diff --git a/drivers/watchdog/bcm63xx_wdt.c b/drivers/watchdog/bcm63xx_wdt.c
index 9989efe..51b855e 100644
--- a/drivers/watchdog/bcm63xx_wdt.c
+++ b/drivers/watchdog/bcm63xx_wdt.c
@@ -22,6 +22,7 @@
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/moduleparam.h>
+#include <linux/mod_devicetable.h>
 #include <linux/spinlock.h>
 #include <linux/types.h>
 #include <linux/watchdog.h>
@@ -31,7 +32,11 @@
 
 #define PFX KBUILD_MODNAME
 
-#define WDT_CLK_NAME		"periph"
+#ifdef CONFIG_BCM63XX
+# define WDT_CLK_NAME		"periph"
+#else
+# define WDT_CLK_NAME		NULL
+#endif
 
 struct bcm63xx_wdt_hw {
 	raw_spinlock_t lock;
@@ -285,12 +290,19 @@ static void bcm63xx_wdt_shutdown(struct platform_device *pdev)
 	bcm63xx_wdt_stop(wdd);
 }
 
+static const struct of_device_id bcm63xx_wdt_dt_ids[] = {
+	{ .compatible = "brcm,bcm6345-wdt" },
+	{}
+};
+MODULE_DEVICE_TABLE(of, bcm63xx_wdt_dt_ids);
+
 static struct platform_driver bcm63xx_wdt_driver = {
 	.probe	= bcm63xx_wdt_probe,
 	.remove = bcm63xx_wdt_remove,
 	.shutdown = bcm63xx_wdt_shutdown,
 	.driver = {
 		.name = "bcm63xx-wdt",
+		.of_match_table = bcm63xx_wdt_dt_ids,
 	}
 };
 
-- 
2.1.4

-- 
Simon Arlott
--
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] 95+ messages in thread

* Re: [PATCH 1/4] clocksource: Add brcm,bcm6345-timer device tree binding
  2015-11-21 19:02 ` Simon Arlott
                   ` (3 preceding siblings ...)
  (?)
@ 2015-11-22 22:12 ` Rob Herring
  -1 siblings, 0 replies; 95+ messages in thread
From: Rob Herring @ 2015-11-22 22:12 UTC (permalink / raw)
  To: Simon Arlott
  Cc: devicetree, Ralf Baechle, Thomas Gleixner, Jason Cooper,
	Marc Zyngier, Kevin Cernekee, Florian Fainelli, Wim Van Sebroeck,
	Miguel Gaio, Maxime Bizon, Linux Kernel Mailing List, linux-mips,
	linux-watchdog, Pawel Moll, Mark Rutland, Ian Campbell,
	Kumar Gala

On Sat, Nov 21, 2015 at 07:02:46PM +0000, Simon Arlott wrote:
> Add device tree binding for the BCM6345 timer. This is required for the
> BCM6345 watchdog which needs to respond to one of the timer interrupts.
> 
> Signed-off-by: Simon Arlott <simon@fire.lp0.eu>

One minor nit, otherwise:

Acked-by: Rob Herring <robh@kernel.org>

> ---
>  .../bindings/timer/brcm,bcm6345-timer.txt          | 57 ++++++++++++++++++++++
>  1 file changed, 57 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/timer/brcm,bcm6345-timer.txt
> 
> diff --git a/Documentation/devicetree/bindings/timer/brcm,bcm6345-timer.txt b/Documentation/devicetree/bindings/timer/brcm,bcm6345-timer.txt
> new file mode 100644
> index 0000000..2593907
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/timer/brcm,bcm6345-timer.txt
> @@ -0,0 +1,57 @@
> +Broadcom BCM6345 Timer
> +
> +This block is a timer that is connected to one interrupt on the main interrupt
> +controller and functions as a programmable interrupt controller for timer events.
> +
> +- 3 to 4 independent timers with their own maskable level interrupt bit (but not
> +  per CPU because there is only one parent interrupt and the timers share it)
> +
> +- 1 watchdog timer with an unmaskable level interrupt
> +
> +- Contains one enable/status word pair
> +
> +- No atomic set/clear operations
> +
> +The lack of per CPU ability of timers makes them unusable as a set of
> +clockevent devices, otherwise they could be attached to the remaining
> +interrupts.
> +
> +The BCM6318 also has a separate interrupt for every timer except the watchdog.
> +
> +Required properties:
> +
> +- compatible: should be "brcm,bcm<soc>-timer", "brcm,bcm6345-timer"
> +- reg: specifies the base physical address and size of the registers, excluding
> +  the watchdog registers
> +- interrupt-controller: identifies the node as an interrupt controller
> +- #interrupt-cells: specifies the number of cells needed to encode an interrupt
> +  source, should be 1.
> +- interrupt-parent: specifies the phandle to the parent interrupt controller(s)
> +  this one is cascaded from
> +- interrupts: specifies the interrupt line(s) in the interrupt-parent controller
> +  node for the main timer interrupt, followed by the individual timer interrupts
> +  if present; valid values depend on the type of parent interrupt controller
> +
> +Example:
> +
> +timer: timer@0x10000080 {

Drop the '0x'

> +        compatible = "brcm,bcm63168-timer", "brcm,bcm6345-timer";
> +        reg = <0x10000080 0x1c>;
> +
> +        interrupt-controller;
> +        #interrupt-cells = <1>;
> +
> +        interrupt-parent = <&periph_intc>;
> +        interrupts = <0>;
> +};
> +
> +timer: timer@0x10000040 {

Ditto.

> +        compatible = "brcm,bcm6318-timer", "brcm,bcm6345-timer";
> +        reg = <0x10000040 0x28>;
> +
> +        interrupt-controller;
> +        #interrupt-cells = <1>;
> +
> +        interrupt-parent = <&periph_intc>;
> +        interrupts = <31>, <0>, <1>, <2>, <3>;
> +};
> -- 
> 2.1.4
> 
> -- 
> Simon Arlott

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

* Re: [PATCH 3/4] watchdog: Add brcm,bcm6345-wdt device tree binding
@ 2015-11-22 22:13     ` Rob Herring
  0 siblings, 0 replies; 95+ messages in thread
From: Rob Herring @ 2015-11-22 22:13 UTC (permalink / raw)
  To: Simon Arlott
  Cc: devicetree, Ralf Baechle, Thomas Gleixner, Jason Cooper,
	Marc Zyngier, Kevin Cernekee, Florian Fainelli, Wim Van Sebroeck,
	Miguel Gaio, Maxime Bizon, Linux Kernel Mailing List, linux-mips,
	linux-watchdog, Pawel Moll, Mark Rutland, Ian Campbell,
	Kumar Gala

On Sat, Nov 21, 2015 at 07:04:39PM +0000, Simon Arlott wrote:
> Add device tree binding for the BCM6345 watchdog.
> 
> This uses the BCM6345 timer for its warning interrupt.
> 
> Signed-off-by: Simon Arlott <simon@fire.lp0.eu>

Acked-by: Rob Herring <robh@kernel.org>

> ---
>  .../bindings/watchdog/brcm,bcm6345-wdt.txt         | 35 ++++++++++++++++++++++
>  1 file changed, 35 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/watchdog/brcm,bcm6345-wdt.txt
> 
> diff --git a/Documentation/devicetree/bindings/watchdog/brcm,bcm6345-wdt.txt b/Documentation/devicetree/bindings/watchdog/brcm,bcm6345-wdt.txt
> new file mode 100644
> index 0000000..9d852d4
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/watchdog/brcm,bcm6345-wdt.txt
> @@ -0,0 +1,35 @@
> +BCM6345 Watchdog timer
> +
> +Required properties:
> +
> +- compatible: should be "brcm,bcm63<soc>-wdt", "brcm,bcm6345-wdt"
> +- reg: Specifies base physical address and size of the registers.
> +- clocks: Specify the clock used for timing
> +
> +Optional properties:
> +
> +- interrupt-parent: phandle to the interrupt controller
> +- interrupts: Specify the interrupt used for the watchdog timout warning
> +- timeout-sec: Contains the default watchdog timeout in seconds
> +
> +Example:
> +
> +watchdog {
> +	compatible = "brcm,bcm63168-wdt", "brcm,bcm6345-wdt";
> +	reg = <0x1000009c 0x0c>;
> +	clocks = <&periph_clk>;
> +
> +	interrupt-parent = <&timer>;
> +	interrupts = <3>;
> +	timeout-sec = <30>;
> +};
> +
> +watchdog {
> +	compatible = "brcm,bcm6318-wdt", "brcm,bcm6345-wdt";
> +	reg = <0x10000068 0x0c>;
> +	clocks = <&periph_clk>;
> +
> +	interrupt-parent = <&timer>;
> +	interrupts = <3>;
> +	timeout-sec = <30>;
> +};
> -- 
> 2.1.4
> 
> -- 
> Simon Arlott

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

* Re: [PATCH 3/4] watchdog: Add brcm,bcm6345-wdt device tree binding
@ 2015-11-22 22:13     ` Rob Herring
  0 siblings, 0 replies; 95+ messages in thread
From: Rob Herring @ 2015-11-22 22:13 UTC (permalink / raw)
  To: Simon Arlott
  Cc: devicetree-u79uwXL29TY76Z2rM5mHXA, Ralf Baechle, Thomas Gleixner,
	Jason Cooper, Marc Zyngier, Kevin Cernekee, Florian Fainelli,
	Wim Van Sebroeck, Miguel Gaio, Maxime Bizon,
	Linux Kernel Mailing List, linux-mips-6z/3iImG2C8G8FEW9MqTrA,
	linux-watchdog-u79uwXL29TY76Z2rM5mHXA, Pawel Moll, Mark Rutland,
	Ian Campbell, Kumar Gala

On Sat, Nov 21, 2015 at 07:04:39PM +0000, Simon Arlott wrote:
> Add device tree binding for the BCM6345 watchdog.
> 
> This uses the BCM6345 timer for its warning interrupt.
> 
> Signed-off-by: Simon Arlott <simon-A6De1vDTPLDsq35pWSNszA@public.gmane.org>

Acked-by: Rob Herring <robh-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>

> ---
>  .../bindings/watchdog/brcm,bcm6345-wdt.txt         | 35 ++++++++++++++++++++++
>  1 file changed, 35 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/watchdog/brcm,bcm6345-wdt.txt
> 
> diff --git a/Documentation/devicetree/bindings/watchdog/brcm,bcm6345-wdt.txt b/Documentation/devicetree/bindings/watchdog/brcm,bcm6345-wdt.txt
> new file mode 100644
> index 0000000..9d852d4
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/watchdog/brcm,bcm6345-wdt.txt
> @@ -0,0 +1,35 @@
> +BCM6345 Watchdog timer
> +
> +Required properties:
> +
> +- compatible: should be "brcm,bcm63<soc>-wdt", "brcm,bcm6345-wdt"
> +- reg: Specifies base physical address and size of the registers.
> +- clocks: Specify the clock used for timing
> +
> +Optional properties:
> +
> +- interrupt-parent: phandle to the interrupt controller
> +- interrupts: Specify the interrupt used for the watchdog timout warning
> +- timeout-sec: Contains the default watchdog timeout in seconds
> +
> +Example:
> +
> +watchdog {
> +	compatible = "brcm,bcm63168-wdt", "brcm,bcm6345-wdt";
> +	reg = <0x1000009c 0x0c>;
> +	clocks = <&periph_clk>;
> +
> +	interrupt-parent = <&timer>;
> +	interrupts = <3>;
> +	timeout-sec = <30>;
> +};
> +
> +watchdog {
> +	compatible = "brcm,bcm6318-wdt", "brcm,bcm6345-wdt";
> +	reg = <0x10000068 0x0c>;
> +	clocks = <&periph_clk>;
> +
> +	interrupt-parent = <&timer>;
> +	interrupts = <3>;
> +	timeout-sec = <30>;
> +};
> -- 
> 2.1.4
> 
> -- 
> Simon Arlott
--
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] 95+ messages in thread

* Re: [PATCH 6/10] watchdog: bcm63xx_wdt: Obtain watchdog clock HZ from "periph" clk
@ 2015-11-23 15:02               ` Jonas Gorski
  0 siblings, 0 replies; 95+ messages in thread
From: Jonas Gorski @ 2015-11-23 15:02 UTC (permalink / raw)
  To: Simon Arlott
  Cc: Guenter Roeck, devicetree, Ralf Baechle, Thomas Gleixner,
	Jason Cooper, Marc Zyngier, Kevin Cernekee, Florian Fainelli,
	Wim Van Sebroeck, Maxime Bizon, Linux Kernel Mailing List,
	MIPS Mailing List, linux-watchdog, Rob Herring, Pawel Moll,
	Mark Rutland, Ian Campbell, Kumar Gala

Hi,

On Sun, Nov 22, 2015 at 3:07 PM, Simon Arlott <simon@fire.lp0.eu> wrote:
> Instead of using a fixed clock HZ in the driver, obtain it from the
> "periph" clk that the watchdog timer uses.
>
> Signed-off-by: Simon Arlott <simon@fire.lp0.eu>
> ---
>  drivers/watchdog/bcm63xx_wdt.c | 36 +++++++++++++++++++++++++++++++-----
>  1 file changed, 31 insertions(+), 5 deletions(-)
>
> diff --git a/drivers/watchdog/bcm63xx_wdt.c b/drivers/watchdog/bcm63xx_wdt.c
> index 1d2a501..eb5e551 100644
> --- a/drivers/watchdog/bcm63xx_wdt.c
> +++ b/drivers/watchdog/bcm63xx_wdt.c
> @@ -13,6 +13,7 @@
>
>  #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
>
> +#include <linux/clk.h>
>  #include <linux/errno.h>
>  #include <linux/io.h>
>  #include <linux/kernel.h>
> @@ -32,11 +33,13 @@
>
>  #define PFX KBUILD_MODNAME
>
> -#define WDT_HZ                 50000000                /* Fclk */
> +#define WDT_CLK_NAME           "periph"

@Florian:
Is this correct? The comment for the watchdog in 6358_map_part.h and
earlier claims that the clock is 40 MHz there, but the code uses 50MHz
- is this a bug in the comments or is it a bug taken over from the
original broadcom code? I'm sure that the periph clock being 50 MHz
even on the older chips is correct, else we'd have noticed that in
serial output (where it's also used).


Jonas

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

* Re: [PATCH 6/10] watchdog: bcm63xx_wdt: Obtain watchdog clock HZ from "periph" clk
@ 2015-11-23 15:02               ` Jonas Gorski
  0 siblings, 0 replies; 95+ messages in thread
From: Jonas Gorski @ 2015-11-23 15:02 UTC (permalink / raw)
  To: Simon Arlott
  Cc: Guenter Roeck, devicetree-u79uwXL29TY76Z2rM5mHXA, Ralf Baechle,
	Thomas Gleixner, Jason Cooper, Marc Zyngier, Kevin Cernekee,
	Florian Fainelli, Wim Van Sebroeck, Maxime Bizon,
	Linux Kernel Mailing List, MIPS Mailing List,
	linux-watchdog-u79uwXL29TY76Z2rM5mHXA, Rob Herring, Pawel Moll,
	Mark Rutland, Ian Campbell, Kumar Gala

Hi,

On Sun, Nov 22, 2015 at 3:07 PM, Simon Arlott <simon-A6De1vDTPLDsq35pWSNszA@public.gmane.org> wrote:
> Instead of using a fixed clock HZ in the driver, obtain it from the
> "periph" clk that the watchdog timer uses.
>
> Signed-off-by: Simon Arlott <simon-A6De1vDTPLDsq35pWSNszA@public.gmane.org>
> ---
>  drivers/watchdog/bcm63xx_wdt.c | 36 +++++++++++++++++++++++++++++++-----
>  1 file changed, 31 insertions(+), 5 deletions(-)
>
> diff --git a/drivers/watchdog/bcm63xx_wdt.c b/drivers/watchdog/bcm63xx_wdt.c
> index 1d2a501..eb5e551 100644
> --- a/drivers/watchdog/bcm63xx_wdt.c
> +++ b/drivers/watchdog/bcm63xx_wdt.c
> @@ -13,6 +13,7 @@
>
>  #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
>
> +#include <linux/clk.h>
>  #include <linux/errno.h>
>  #include <linux/io.h>
>  #include <linux/kernel.h>
> @@ -32,11 +33,13 @@
>
>  #define PFX KBUILD_MODNAME
>
> -#define WDT_HZ                 50000000                /* Fclk */
> +#define WDT_CLK_NAME           "periph"

@Florian:
Is this correct? The comment for the watchdog in 6358_map_part.h and
earlier claims that the clock is 40 MHz there, but the code uses 50MHz
- is this a bug in the comments or is it a bug taken over from the
original broadcom code? I'm sure that the periph clock being 50 MHz
even on the older chips is correct, else we'd have noticed that in
serial output (where it's also used).


Jonas
--
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	[flat|nested] 95+ messages in thread

* Re: [PATCH 1/4] clocksource: Add brcm,bcm6345-timer device tree binding
  2015-11-21 19:02 ` Simon Arlott
                   ` (4 preceding siblings ...)
  (?)
@ 2015-11-23 15:33 ` Jonas Gorski
  2015-11-23 18:55     ` Simon Arlott
  -1 siblings, 1 reply; 95+ messages in thread
From: Jonas Gorski @ 2015-11-23 15:33 UTC (permalink / raw)
  To: Simon Arlott
  Cc: devicetree, Ralf Baechle, Thomas Gleixner, Jason Cooper,
	Marc Zyngier, Kevin Cernekee, Florian Fainelli, Wim Van Sebroeck,
	Miguel Gaio, Maxime Bizon, Linux Kernel Mailing List,
	MIPS Mailing List, linux-watchdog, Rob Herring, Pawel Moll,
	Mark Rutland, Ian Campbell, Kumar Gala

On Sat, Nov 21, 2015 at 8:02 PM, Simon Arlott <simon@fire.lp0.eu> wrote:
> Add device tree binding for the BCM6345 timer. This is required for the
> BCM6345 watchdog which needs to respond to one of the timer interrupts.
>
> Signed-off-by: Simon Arlott <simon@fire.lp0.eu>
> ---
>  .../bindings/timer/brcm,bcm6345-timer.txt          | 57 ++++++++++++++++++++++
>  1 file changed, 57 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/timer/brcm,bcm6345-timer.txt
>
> diff --git a/Documentation/devicetree/bindings/timer/brcm,bcm6345-timer.txt b/Documentation/devicetree/bindings/timer/brcm,bcm6345-timer.txt
> new file mode 100644
> index 0000000..2593907
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/timer/brcm,bcm6345-timer.txt
> @@ -0,0 +1,57 @@
> +Broadcom BCM6345 Timer
> +
> +This block is a timer that is connected to one interrupt on the main interrupt
> +controller and functions as a programmable interrupt controller for timer events.
> +
> +- 3 to 4 independent timers with their own maskable level interrupt bit (but not
> +  per CPU because there is only one parent interrupt and the timers share it)
> +

This is true for the 6345 one but not the 6318/63148/63381 one, which
has one interrupt per timer (+ one extra watchdog interrupt on
6318/63148), and the parent interrupt controller supports affinity. So
you could e.g. route the timer 0 irq to cpu 0 and timer1 irq on cpu 1.

> +- 1 watchdog timer with an unmaskable level interrupt
> +
> +- Contains one enable/status word pair
> +
> +- No atomic set/clear operations
> +
> +The lack of per CPU ability of timers makes them unusable as a set of
> +clockevent devices, otherwise they could be attached to the remaining
> +interrupts.
> +
> +The BCM6318 also has a separate interrupt for every timer except the watchdog.
> +
> +Required properties:
> +
> +- compatible: should be "brcm,bcm<soc>-timer", "brcm,bcm6345-timer"

Since bcm6318 uses a slightly different register layout than the
earlier SoCs, I'd argue that using bcm6345-timer as a compatible for
bcm6318 is wrong.


Jonas

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

* Re: [PATCH 6/10] watchdog: bcm63xx_wdt: Obtain watchdog clock HZ from "periph" clk
@ 2015-11-23 18:19                 ` Florian Fainelli
  0 siblings, 0 replies; 95+ messages in thread
From: Florian Fainelli @ 2015-11-23 18:19 UTC (permalink / raw)
  To: Jonas Gorski, Simon Arlott
  Cc: Guenter Roeck, devicetree, Ralf Baechle, Thomas Gleixner,
	Jason Cooper, Marc Zyngier, Kevin Cernekee, Wim Van Sebroeck,
	Maxime Bizon, Linux Kernel Mailing List, MIPS Mailing List,
	linux-watchdog, Rob Herring, Pawel Moll, Mark Rutland,
	Ian Campbell, Kumar Gala

On 23/11/15 07:02, Jonas Gorski wrote:
> Hi,
> 
> On Sun, Nov 22, 2015 at 3:07 PM, Simon Arlott <simon@fire.lp0.eu> wrote:
>> Instead of using a fixed clock HZ in the driver, obtain it from the
>> "periph" clk that the watchdog timer uses.
>>
>> Signed-off-by: Simon Arlott <simon@fire.lp0.eu>
>> ---
>>  drivers/watchdog/bcm63xx_wdt.c | 36 +++++++++++++++++++++++++++++++-----
>>  1 file changed, 31 insertions(+), 5 deletions(-)
>>
>> diff --git a/drivers/watchdog/bcm63xx_wdt.c b/drivers/watchdog/bcm63xx_wdt.c
>> index 1d2a501..eb5e551 100644
>> --- a/drivers/watchdog/bcm63xx_wdt.c
>> +++ b/drivers/watchdog/bcm63xx_wdt.c
>> @@ -13,6 +13,7 @@
>>
>>  #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
>>
>> +#include <linux/clk.h>
>>  #include <linux/errno.h>
>>  #include <linux/io.h>
>>  #include <linux/kernel.h>
>> @@ -32,11 +33,13 @@
>>
>>  #define PFX KBUILD_MODNAME
>>
>> -#define WDT_HZ                 50000000                /* Fclk */
>> +#define WDT_CLK_NAME           "periph"
> 
> @Florian:
> Is this correct? The comment for the watchdog in 6358_map_part.h and
> earlier claims that the clock is 40 MHz there, but the code uses 50MHz
> - is this a bug in the comments or is it a bug taken over from the
> original broadcom code? I'm sure that the periph clock being 50 MHz
> even on the older chips is correct, else we'd have noticed that in
> serial output (where it's also used).

There are references to a Fbus2 clock in documentation, but I could not
find any actual documentation for its actual clock frequency, I would be
surprised if this chip would have diverged from the previous and future
ones and used a 40Mhz clock. 6345 started with a peripheral clock
running at 50Mhz, and that is true for all chips since then AFAICT.

I agree we would have noticed this with the UART or SPI controllers if
that was not true, so probably a code glitch here...
-- 
Florian

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

* Re: [PATCH 6/10] watchdog: bcm63xx_wdt: Obtain watchdog clock HZ from "periph" clk
@ 2015-11-23 18:19                 ` Florian Fainelli
  0 siblings, 0 replies; 95+ messages in thread
From: Florian Fainelli @ 2015-11-23 18:19 UTC (permalink / raw)
  To: Jonas Gorski, Simon Arlott
  Cc: Guenter Roeck, devicetree-u79uwXL29TY76Z2rM5mHXA, Ralf Baechle,
	Thomas Gleixner, Jason Cooper, Marc Zyngier, Kevin Cernekee,
	Wim Van Sebroeck, Maxime Bizon, Linux Kernel Mailing List,
	MIPS Mailing List, linux-watchdog-u79uwXL29TY76Z2rM5mHXA,
	Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala

On 23/11/15 07:02, Jonas Gorski wrote:
> Hi,
> 
> On Sun, Nov 22, 2015 at 3:07 PM, Simon Arlott <simon-A6De1vDTPLDsq35pWSNszA@public.gmane.org> wrote:
>> Instead of using a fixed clock HZ in the driver, obtain it from the
>> "periph" clk that the watchdog timer uses.
>>
>> Signed-off-by: Simon Arlott <simon-A6De1vDTPLDsq35pWSNszA@public.gmane.org>
>> ---
>>  drivers/watchdog/bcm63xx_wdt.c | 36 +++++++++++++++++++++++++++++++-----
>>  1 file changed, 31 insertions(+), 5 deletions(-)
>>
>> diff --git a/drivers/watchdog/bcm63xx_wdt.c b/drivers/watchdog/bcm63xx_wdt.c
>> index 1d2a501..eb5e551 100644
>> --- a/drivers/watchdog/bcm63xx_wdt.c
>> +++ b/drivers/watchdog/bcm63xx_wdt.c
>> @@ -13,6 +13,7 @@
>>
>>  #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
>>
>> +#include <linux/clk.h>
>>  #include <linux/errno.h>
>>  #include <linux/io.h>
>>  #include <linux/kernel.h>
>> @@ -32,11 +33,13 @@
>>
>>  #define PFX KBUILD_MODNAME
>>
>> -#define WDT_HZ                 50000000                /* Fclk */
>> +#define WDT_CLK_NAME           "periph"
> 
> @Florian:
> Is this correct? The comment for the watchdog in 6358_map_part.h and
> earlier claims that the clock is 40 MHz there, but the code uses 50MHz
> - is this a bug in the comments or is it a bug taken over from the
> original broadcom code? I'm sure that the periph clock being 50 MHz
> even on the older chips is correct, else we'd have noticed that in
> serial output (where it's also used).

There are references to a Fbus2 clock in documentation, but I could not
find any actual documentation for its actual clock frequency, I would be
surprised if this chip would have diverged from the previous and future
ones and used a 40Mhz clock. 6345 started with a peripheral clock
running at 50Mhz, and that is true for all chips since then AFAICT.

I agree we would have noticed this with the UART or SPI controllers if
that was not true, so probably a code glitch here...
-- 
Florian
--
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	[flat|nested] 95+ messages in thread

* [PATCH (v2) 1/10] clocksource: Add brcm,bcm6345-timer device tree binding
@ 2015-11-23 18:55     ` Simon Arlott
  0 siblings, 0 replies; 95+ messages in thread
From: Simon Arlott @ 2015-11-23 18:55 UTC (permalink / raw)
  To: Jonas Gorski
  Cc: devicetree, Ralf Baechle, Thomas Gleixner, Jason Cooper,
	Marc Zyngier, Kevin Cernekee, Florian Fainelli, Wim Van Sebroeck,
	Miguel Gaio, Maxime Bizon, Linux Kernel Mailing List,
	MIPS Mailing List, linux-watchdog, Rob Herring, Pawel Moll,
	Mark Rutland, Ian Campbell, Kumar Gala

Add device tree bindings for the BCM6345/BCM6318 timers. This is required
for the BCM6345 watchdog which needs to respond to one of the timer
interrupts.

Signed-off-by: Simon Arlott <simon@fire.lp0.eu>
---
On 23/11/15 15:33, Jonas Gorski wrote:
> On Sat, Nov 21, 2015 at 8:02 PM, Simon Arlott <simon@fire.lp0.eu> wrote:
>> +- compatible: should be "brcm,bcm<soc>-timer", "brcm,bcm6345-timer"
> 
> Since bcm6318 uses a slightly different register layout than the
> earlier SoCs, I'd argue that using bcm6345-timer as a compatible for
> bcm6318 is wrong.

I've split them out into two very similar bindings.

Patches 1/4 and 2/4 are replaced with (v2) 1/10 and (v2) 2/10.

 .../bindings/timer/brcm,bcm6318-timer.txt          | 44 ++++++++++++++++++++
 .../bindings/timer/brcm,bcm6345-timer.txt          | 47 ++++++++++++++++++++++
 2 files changed, 91 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/timer/brcm,bcm6318-timer.txt
 create mode 100644 Documentation/devicetree/bindings/timer/brcm,bcm6345-timer.txt

diff --git a/Documentation/devicetree/bindings/timer/brcm,bcm6318-timer.txt b/Documentation/devicetree/bindings/timer/brcm,bcm6318-timer.txt
new file mode 100644
index 0000000..cf4be7e
--- /dev/null
+++ b/Documentation/devicetree/bindings/timer/brcm,bcm6318-timer.txt
@@ -0,0 +1,44 @@
+Broadcom BCM6318 Timer
+
+This block is a timer that is connected to multiple interrupts on the main
+interrupt controller and functions as a programmable interrupt controller for
+timer events. There is a main timer interrupt for all timers.
+
+- 4 independent timers with their own interrupt, and own maskable level
+  interrupt bit in the main timer interrupt
+
+- 1 watchdog timer with an unmaskable level interrupt bit in the main timer
+  interrupt
+
+- Contains one enable/status word pair
+
+- No atomic set/clear operations
+
+Required properties:
+
+- compatible: should be "brcm,bcm<soc>-timer", "brcm,bcm6318-timer"
+- reg: specifies the base physical address and size of the registers, excluding
+  the watchdog registers
+- interrupt-controller: identifies the node as an interrupt controller
+- #interrupt-cells: specifies the number of cells needed to encode an interrupt
+  source, should be 1.
+- interrupt-parent: specifies the phandle to the parent interrupt controller(s)
+  this one is cascaded from
+- interrupts: specifies the interrupt line(s) in the interrupt-parent controller
+  node for the main timer interrupt, followed by the individual timer
+  interrupts; valid values depend on the type of parent interrupt controller
+- clocks: phandle of timer reference clock (periph)
+
+Example:
+
+timer: timer@10000040 {
+	compatible = "brcm,bcm63148-timer", "brcm,bcm6318-timer";
+	reg = <0x10000040 0x28>;
+
+	interrupt-controller;
+	#interrupt-cells = <1>;
+
+	interrupt-parent = <&periph_intc>;
+	interrupts = <31>, <0>, <1>, <2>, <3>;
+	clock = <&periph_osc>;
+};
diff --git a/Documentation/devicetree/bindings/timer/brcm,bcm6345-timer.txt b/Documentation/devicetree/bindings/timer/brcm,bcm6345-timer.txt
new file mode 100644
index 0000000..03250dd
--- /dev/null
+++ b/Documentation/devicetree/bindings/timer/brcm,bcm6345-timer.txt
@@ -0,0 +1,47 @@
+Broadcom BCM6345 Timer
+
+This block is a timer that is connected to one interrupt on the main interrupt
+controller and functions as a programmable interrupt controller for timer
+events.
+
+- 3 independent timers with their own maskable level interrupt bit (but not
+  per CPU because there is only one parent interrupt and the timers share it)
+
+- 1 watchdog timer with an unmaskable level interrupt
+
+- Contains one enable/status word pair
+
+- No atomic set/clear operations
+
+The lack of per CPU ability of timers makes them unusable as a set of
+clockevent devices, otherwise they could be attached to the remaining
+interrupts.
+
+Required properties:
+
+- compatible: should be "brcm,bcm<soc>-timer", "brcm,bcm6345-timer"
+- reg: specifies the base physical address and size of the registers, excluding
+  the watchdog registers
+- interrupt-controller: identifies the node as an interrupt controller
+- #interrupt-cells: specifies the number of cells needed to encode an interrupt
+  source, should be 1.
+- interrupt-parent: specifies the phandle to the parent interrupt controller(s)
+  this one is cascaded from
+- interrupts: specifies the interrupt line(s) in the interrupt-parent controller
+  node for the timer interrupt; valid values depend on the type of parent
+  interrupt controller
+- clocks: phandle of timer reference clock (periph)
+
+Example:
+
+timer: timer@10000080 {
+	compatible = "brcm,bcm63168-timer", "brcm,bcm6345-timer";
+	reg = <0x10000080 0x1c>;
+
+	interrupt-controller;
+	#interrupt-cells = <1>;
+
+	interrupt-parent = <&periph_intc>;
+	interrupts = <0>;
+	clock·=·<&periph_osc>;
+};
-- 
2.1.4

-- 
Simon Arlott

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

* [PATCH (v2) 1/10] clocksource: Add brcm,bcm6345-timer device tree binding
@ 2015-11-23 18:55     ` Simon Arlott
  0 siblings, 0 replies; 95+ messages in thread
From: Simon Arlott @ 2015-11-23 18:55 UTC (permalink / raw)
  To: Jonas Gorski
  Cc: devicetree-u79uwXL29TY76Z2rM5mHXA, Ralf Baechle, Thomas Gleixner,
	Jason Cooper, Marc Zyngier, Kevin Cernekee, Florian Fainelli,
	Wim Van Sebroeck, Miguel Gaio, Maxime Bizon,
	Linux Kernel Mailing List, MIPS Mailing List,
	linux-watchdog-u79uwXL29TY76Z2rM5mHXA, Rob Herring, Pawel Moll,
	Mark Rutland, Ian Campbell, Kumar Gala

Add device tree bindings for the BCM6345/BCM6318 timers. This is required
for the BCM6345 watchdog which needs to respond to one of the timer
interrupts.

Signed-off-by: Simon Arlott <simon-A6De1vDTPLDsq35pWSNszA@public.gmane.org>
---
On 23/11/15 15:33, Jonas Gorski wrote:
> On Sat, Nov 21, 2015 at 8:02 PM, Simon Arlott <simon-A6De1vDTPLDsq35pWSNszA@public.gmane.org> wrote:
>> +- compatible: should be "brcm,bcm<soc>-timer", "brcm,bcm6345-timer"
> 
> Since bcm6318 uses a slightly different register layout than the
> earlier SoCs, I'd argue that using bcm6345-timer as a compatible for
> bcm6318 is wrong.

I've split them out into two very similar bindings.

Patches 1/4 and 2/4 are replaced with (v2) 1/10 and (v2) 2/10.

 .../bindings/timer/brcm,bcm6318-timer.txt          | 44 ++++++++++++++++++++
 .../bindings/timer/brcm,bcm6345-timer.txt          | 47 ++++++++++++++++++++++
 2 files changed, 91 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/timer/brcm,bcm6318-timer.txt
 create mode 100644 Documentation/devicetree/bindings/timer/brcm,bcm6345-timer.txt

diff --git a/Documentation/devicetree/bindings/timer/brcm,bcm6318-timer.txt b/Documentation/devicetree/bindings/timer/brcm,bcm6318-timer.txt
new file mode 100644
index 0000000..cf4be7e
--- /dev/null
+++ b/Documentation/devicetree/bindings/timer/brcm,bcm6318-timer.txt
@@ -0,0 +1,44 @@
+Broadcom BCM6318 Timer
+
+This block is a timer that is connected to multiple interrupts on the main
+interrupt controller and functions as a programmable interrupt controller for
+timer events. There is a main timer interrupt for all timers.
+
+- 4 independent timers with their own interrupt, and own maskable level
+  interrupt bit in the main timer interrupt
+
+- 1 watchdog timer with an unmaskable level interrupt bit in the main timer
+  interrupt
+
+- Contains one enable/status word pair
+
+- No atomic set/clear operations
+
+Required properties:
+
+- compatible: should be "brcm,bcm<soc>-timer", "brcm,bcm6318-timer"
+- reg: specifies the base physical address and size of the registers, excluding
+  the watchdog registers
+- interrupt-controller: identifies the node as an interrupt controller
+- #interrupt-cells: specifies the number of cells needed to encode an interrupt
+  source, should be 1.
+- interrupt-parent: specifies the phandle to the parent interrupt controller(s)
+  this one is cascaded from
+- interrupts: specifies the interrupt line(s) in the interrupt-parent controller
+  node for the main timer interrupt, followed by the individual timer
+  interrupts; valid values depend on the type of parent interrupt controller
+- clocks: phandle of timer reference clock (periph)
+
+Example:
+
+timer: timer@10000040 {
+	compatible = "brcm,bcm63148-timer", "brcm,bcm6318-timer";
+	reg = <0x10000040 0x28>;
+
+	interrupt-controller;
+	#interrupt-cells = <1>;
+
+	interrupt-parent = <&periph_intc>;
+	interrupts = <31>, <0>, <1>, <2>, <3>;
+	clock = <&periph_osc>;
+};
diff --git a/Documentation/devicetree/bindings/timer/brcm,bcm6345-timer.txt b/Documentation/devicetree/bindings/timer/brcm,bcm6345-timer.txt
new file mode 100644
index 0000000..03250dd
--- /dev/null
+++ b/Documentation/devicetree/bindings/timer/brcm,bcm6345-timer.txt
@@ -0,0 +1,47 @@
+Broadcom BCM6345 Timer
+
+This block is a timer that is connected to one interrupt on the main interrupt
+controller and functions as a programmable interrupt controller for timer
+events.
+
+- 3 independent timers with their own maskable level interrupt bit (but not
+  per CPU because there is only one parent interrupt and the timers share it)
+
+- 1 watchdog timer with an unmaskable level interrupt
+
+- Contains one enable/status word pair
+
+- No atomic set/clear operations
+
+The lack of per CPU ability of timers makes them unusable as a set of
+clockevent devices, otherwise they could be attached to the remaining
+interrupts.
+
+Required properties:
+
+- compatible: should be "brcm,bcm<soc>-timer", "brcm,bcm6345-timer"
+- reg: specifies the base physical address and size of the registers, excluding
+  the watchdog registers
+- interrupt-controller: identifies the node as an interrupt controller
+- #interrupt-cells: specifies the number of cells needed to encode an interrupt
+  source, should be 1.
+- interrupt-parent: specifies the phandle to the parent interrupt controller(s)
+  this one is cascaded from
+- interrupts: specifies the interrupt line(s) in the interrupt-parent controller
+  node for the timer interrupt; valid values depend on the type of parent
+  interrupt controller
+- clocks: phandle of timer reference clock (periph)
+
+Example:
+
+timer: timer@10000080 {
+	compatible = "brcm,bcm63168-timer", "brcm,bcm6345-timer";
+	reg = <0x10000080 0x1c>;
+
+	interrupt-controller;
+	#interrupt-cells = <1>;
+
+	interrupt-parent = <&periph_intc>;
+	interrupts = <0>;
+	clock·=·<&periph_osc>;
+};
-- 
2.1.4

-- 
Simon Arlott
--
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] 95+ messages in thread

* [PATCH (v2) 2/10] MIPS: bmips: Add bcm6345-l2-timer interrupt controller
@ 2015-11-23 18:57       ` Simon Arlott
  0 siblings, 0 replies; 95+ messages in thread
From: Simon Arlott @ 2015-11-23 18:57 UTC (permalink / raw)
  To: Jonas Gorski
  Cc: devicetree, Ralf Baechle, Thomas Gleixner, Jason Cooper,
	Marc Zyngier, Kevin Cernekee, Florian Fainelli, Wim Van Sebroeck,
	Miguel Gaio, Maxime Bizon, Linux Kernel Mailing List,
	MIPS Mailing List, linux-watchdog, Rob Herring, Pawel Moll,
	Mark Rutland, Ian Campbell, Kumar Gala

Add the BCM6345/BCM6318 timer as an interrupt controller so that it can be
used by the watchdog to warn that its timer will expire soon.

Support for clocksource/clockevents is not implemented as the timer
interrupt is not per CPU (except on the BCM6318) and the MIPS clock is
better. This could be added later if required without changing the device
tree binding.

Signed-off-by: Simon Arlott <simon@fire.lp0.eu>
---
On 23/11/15 15:33, Jonas Gorski wrote:
> > On Sat, Nov 21, 2015 at 8:02 PM, Simon Arlott <simon@fire.lp0.eu> wrote:
>> >> +- compatible: should be "brcm,bcm<soc>-timer", "brcm,bcm6345-timer"
> > 
> > Since bcm6318 uses a slightly different register layout than the
> > earlier SoCs, I'd argue that using bcm6345-timer as a compatible for
> > bcm6318 is wrong.

I've updated the driver so that it outputs "BCM6318" instead of
"BCM6345" depending on which compatible string was matched.

Patches 1/4 and 2/4 are replaced with (v2) 1/10 and (v2) 2/10.

 drivers/irqchip/Kconfig                |   5 +
 drivers/irqchip/Makefile               |   1 +
 drivers/irqchip/irq-bcm6345-l2-timer.c | 321 +++++++++++++++++++++++++++++++++
 3 files changed, 327 insertions(+)
 create mode 100644 drivers/irqchip/irq-bcm6345-l2-timer.c

diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
index d307bb3..21c3d9b 100644
--- a/drivers/irqchip/Kconfig
+++ b/drivers/irqchip/Kconfig
@@ -70,6 +70,11 @@ config BCM6345_L1_IRQ
 	select GENERIC_IRQ_CHIP
 	select IRQ_DOMAIN
 
+config BCM6345_L2_TIMER_IRQ
+	bool
+	select GENERIC_IRQ_CHIP
+	select IRQ_DOMAIN
+
 config BCM7038_L1_IRQ
 	bool
 	select GENERIC_IRQ_CHIP
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index ded59cf..2687dea 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -44,6 +44,7 @@ obj-$(CONFIG_XTENSA_MX)			+= irq-xtensa-mx.o
 obj-$(CONFIG_IRQ_CROSSBAR)		+= irq-crossbar.o
 obj-$(CONFIG_SOC_VF610)			+= irq-vf610-mscm-ir.o
 obj-$(CONFIG_BCM6345_L1_IRQ)		+= irq-bcm6345-l1.o
+obj-$(CONFIG_BCM6345_L2_TIMER_IRQ)	+= irq-bcm6345-l2-timer.o
 obj-$(CONFIG_BCM7038_L1_IRQ)		+= irq-bcm7038-l1.o
 obj-$(CONFIG_BCM7120_L2_IRQ)		+= irq-bcm7120-l2.o
 obj-$(CONFIG_BRCMSTB_L2_IRQ)		+= irq-brcmstb-l2.o
diff --git a/drivers/irqchip/irq-bcm6345-l2-timer.c b/drivers/irqchip/irq-bcm6345-l2-timer.c
new file mode 100644
index 0000000..b7854f0
--- /dev/null
+++ b/drivers/irqchip/irq-bcm6345-l2-timer.c
@@ -0,0 +1,321 @@
+/*
+ * Copyright 2015 Simon Arlott
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * Based on arch/mips/bcm63xx/timer.c:
+ * Copyright (C) 2008 Maxime Bizon <mbizon@freebox.fr>
+ *
+ * Registers for SoCs with 4 timers: BCM6345, BCM6328, BCM6362, BCM6816,
+ *                                   BCM68220,BCM63168, BCM63268
+ *   0x02: IRQ enable (u8)
+ *   0x03: IRQ status (u8)
+ *   0x04: Timer 0 control
+ *   0x08: Timer 1 control
+ *   0x0c: Timer 2 control
+ *   0x10: Timer 0 count
+ *   0x14: Timer 1 count
+ *   0x18: Timer 2 count
+ *   0x1c+: Watchdog registers
+ *
+ * Registers for SoCs with 5 timers: BCM6318
+ *   0x00: IRQ enable (u32)
+ *   0x04: IRQ status (u32)
+ *   0x08: Timer 0 control
+ *   0x0c: Timer 1 control
+ *   0x10: Timer 2 control
+ *   0x14: Timer 3 control
+ *   0x18: Timer 0 count
+ *   0x1c: Timer 1 count
+ *   0x20: Timer 2 count
+ *   0x24: Timer 3 count
+ *   0x28+: Watchdog registers
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/bitops.h>
+#include <linux/interrupt.h>
+#include <linux/irqreturn.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/of_platform.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/string.h>
+#include <linux/irqchip.h>
+#include <linux/irqchip/chained_irq.h>
+
+#define REG_6345_IRQ_ENABLE		0x02
+#define REG_6345_IRQ_STATUS		0x03
+#define REG_6345_CFG_BASE		0x04
+
+#define REG_6318_IRQ_ENABLE		0x00
+#define REG_6318_IRQ_STATUS		0x04
+#define REG_6318_CFG_BASE		0x08
+
+#define NR_TIMERS_6345			4
+#define WDT_TIMER_ID_6345		(NR_TIMERS_6345 - 1)
+
+#define NR_TIMERS_6318			5
+#define WDT_TIMER_ID_6318		(NR_TIMERS_6318 - 1)
+
+/* Per-timer count register */
+#define COUNT_MASK			(0x3fffffff)
+
+/* Per-timer control register */
+#define CONTROL_COUNTDOWN_MASK		(0x3fffffff)
+#define CONTROL_RSTCNTCLR_MASK		(1 << 30)
+#define CONTROL_ENABLE_MASK		(1 << 31)
+
+enum bcm6345_timer_type {
+	TIMER_TYPE_6345,
+	TIMER_TYPE_6318,
+};
+
+struct bcm6345_timer {
+	raw_spinlock_t lock;
+	void __iomem *base;
+	unsigned int irq;
+	struct irq_domain *domain;
+
+	enum bcm6345_timer_type type;
+	unsigned int nr_timers;
+	/* The watchdog timer has separate control/remaining registers
+	 * and cannot be masked.
+	 */
+	int wdt_timer_id;
+};
+
+static inline u32 bcm6345_timer_read_int_status(struct bcm6345_timer *timer)
+{
+	if (timer->type == TIMER_TYPE_6318)
+		return __raw_readl(timer->base + REG_6318_IRQ_STATUS);
+	else
+		return __raw_readb(timer->base + REG_6345_IRQ_STATUS);
+}
+
+static inline void bcm6345_timer_write_int_status(struct bcm6345_timer *timer,
+	u32 val)
+{
+	if (timer->type == TIMER_TYPE_6318)
+		__raw_writel(val, timer->base + REG_6318_IRQ_STATUS);
+	else
+		__raw_writeb(val, timer->base + REG_6345_IRQ_STATUS);
+}
+
+static inline u32 bcm6345_timer_read_int_enable(struct bcm6345_timer *timer)
+{
+	if (timer->type == TIMER_TYPE_6318)
+		return __raw_readl(timer->base + REG_6318_IRQ_ENABLE);
+	else
+		return __raw_readb(timer->base + REG_6345_IRQ_ENABLE);
+}
+
+static inline void bcm6345_timer_write_int_enable(struct bcm6345_timer *timer,
+	u32 val)
+{
+	if (timer->type == TIMER_TYPE_6318)
+		__raw_writel(val, timer->base + REG_6318_IRQ_ENABLE);
+	else
+		__raw_writeb(val, timer->base + REG_6345_IRQ_ENABLE);
+}
+
+static inline void bcm6345_timer_write_control(struct bcm6345_timer *timer,
+	unsigned int id, u32 val)
+{
+	if (timer->type == TIMER_TYPE_6318)
+		__raw_writel(0, timer->base + REG_6318_CFG_BASE + id * 4);
+	else
+		__raw_writel(0, timer->base + REG_6345_CFG_BASE + id * 4);
+}
+
+static inline void bcm6345_timer_write_count(struct bcm6345_timer *timer,
+	unsigned int id, u32 val)
+{
+	/* Count registers are immediately after the control registers */
+	return bcm6345_timer_write_control(timer, timer->nr_timers + id, val);
+}
+
+static inline void bcm6345_timer_stop(struct bcm6345_timer *timer, int id)
+{
+	if (id != timer->wdt_timer_id) {
+		bcm6345_timer_write_control(timer, id, 0);
+		bcm6345_timer_write_count(timer, id, 0);
+		bcm6345_timer_write_int_status(timer, BIT(id));
+	}
+}
+
+static void bcm6345_timer_interrupt(struct irq_desc *desc)
+{
+	struct bcm6345_timer *timer = irq_desc_get_handler_data(desc);
+	struct irq_chip *chip = irq_desc_get_chip(desc);
+	unsigned long pending;
+	irq_hw_number_t hwirq;
+	unsigned int irq;
+
+	chained_irq_enter(chip, desc);
+
+	pending = bcm6345_timer_read_int_status(timer);
+	pending &= bcm6345_timer_read_int_enable(timer) |
+			BIT(timer->wdt_timer_id); /* Watchdog can't be masked */
+
+	for_each_set_bit(hwirq, &pending, timer->nr_timers) {
+		irq = irq_linear_revmap(timer->domain, hwirq);
+		if (irq)
+			do_IRQ(irq);
+		else
+			spurious_interrupt();
+	}
+
+	chained_irq_exit(chip, desc);
+}
+
+static void bcm6345_timer_unmask(struct irq_data *d)
+{
+	struct bcm6345_timer *timer = irq_data_get_irq_chip_data(d);
+	unsigned long flags;
+	u8 val;
+
+	if (d->hwirq != timer->wdt_timer_id) {
+		raw_spin_lock_irqsave(&timer->lock, flags);
+		val = bcm6345_timer_read_int_enable(timer);
+		val |= BIT(d->hwirq);
+		bcm6345_timer_write_int_enable(timer, val);
+		raw_spin_unlock_irqrestore(&timer->lock, flags);
+	}
+}
+
+static void bcm6345_timer_mask(struct irq_data *d)
+{
+	struct bcm6345_timer *timer = irq_data_get_irq_chip_data(d);
+	unsigned long flags;
+	u32 val;
+
+	if (d->hwirq != timer->wdt_timer_id) {
+		raw_spin_lock_irqsave(&timer->lock, flags);
+		val = bcm6345_timer_read_int_enable(timer);
+		val &= ~BIT(d->hwirq);
+		bcm6345_timer_write_int_enable(timer, val);
+		raw_spin_unlock_irqrestore(&timer->lock, flags);
+	}
+}
+
+static void bcm6345_timer_eoi(struct irq_data *d)
+{
+	struct bcm6345_timer *timer = irq_data_get_irq_chip_data(d);
+
+	if (d->hwirq != timer->wdt_timer_id)
+		bcm6345_timer_write_int_status(timer, BIT(d->hwirq));
+}
+
+static struct irq_chip bcm6345_timer_chip = {
+	.name			= "bcm6345-timer",
+	.irq_mask		= bcm6345_timer_mask,
+	.irq_unmask		= bcm6345_timer_unmask,
+	.irq_eoi		= bcm6345_timer_eoi,
+};
+
+static int bcm6345_timer_map(struct irq_domain *d, unsigned int virq,
+			     irq_hw_number_t hwirq)
+{
+	struct bcm6345_timer *timer = d->host_data;
+
+	irq_set_chip_and_handler(virq, &bcm6345_timer_chip,
+		hwirq == timer->wdt_timer_id ?
+			handle_simple_irq : handle_fasteoi_irq);
+	irq_set_chip_data(virq, timer);
+	return 0;
+}
+
+static const struct irq_domain_ops bcm6345_timer_domain_ops = {
+	.xlate			= irq_domain_xlate_onecell,
+	.map			= bcm6345_timer_map,
+};
+
+static int __init bcm63xx_timer_init(struct device_node *node,
+	enum bcm6345_timer_type type, unsigned int nr_timers, int wdt_timer_id)
+{
+	struct bcm6345_timer *timer;
+	int ret, i;
+
+	timer = kzalloc(sizeof(*timer), GFP_KERNEL);
+	if (!timer)
+		return -ENOMEM;
+
+	raw_spin_lock_init(&timer->lock);
+	timer->type = type;
+	timer->nr_timers = nr_timers;
+	timer->wdt_timer_id = wdt_timer_id;
+
+	timer->irq = irq_of_parse_and_map(node, 0);
+	if (!timer->irq) {
+		pr_err("unable to map parent IRQ\n");
+		ret = -EINVAL;
+		goto free_timer;
+	}
+
+	timer->base = of_iomap(node, 0);
+	if (!timer->base) {
+		pr_err("unable to remap registers\n");
+		ret = -ENOMEM;
+		goto free_timer;
+	}
+
+	timer->domain = irq_domain_add_linear(node, timer->nr_timers,
+					&bcm6345_timer_domain_ops, timer);
+	if (!timer->domain) {
+		pr_err("unable to add IRQ domain");
+		ret = -ENOMEM;
+		goto unmap_io;
+	}
+
+	/* Mask all interrupts and stop all timers */
+	bcm6345_timer_write_int_enable(timer, 0);
+	for (i = 0; i < timer->nr_timers; i++)
+		bcm6345_timer_stop(timer, i);
+
+	irq_set_chained_handler_and_data(timer->irq,
+					bcm6345_timer_interrupt, timer);
+
+	if (timer->type == TIMER_TYPE_6318)
+		pr_info("registered BCM6318 L2 (timer) intc at MMIO 0x%p (irq = %d, IRQs: %d)\n",
+				timer->base, timer->irq, timer->nr_timers);
+	else
+		pr_info("registered BCM6345 L2 (timer) intc at MMIO 0x%p (irq = %d, IRQs: %d)\n",
+				timer->base, timer->irq, timer->nr_timers);
+	return 0;
+
+unmap_io:
+	iounmap(timer->base);
+free_timer:
+	kfree(timer);
+	return ret;
+}
+
+static int __init bcm6318_timer_init(struct device_node *node,
+				      struct device_node *parent)
+{
+	return bcm63xx_timer_init(node, TIMER_TYPE_6318,
+				NR_TIMERS_6318, WDT_TIMER_ID_6318);
+}
+
+static int __init bcm6345_timer_init(struct device_node *node,
+				      struct device_node *parent)
+{
+	return bcm63xx_timer_init(node, TIMER_TYPE_6345,
+				NR_TIMERS_6345, WDT_TIMER_ID_6345);
+}
+
+IRQCHIP_DECLARE(bcm6318_l2_timer, "brcm,bcm6318-timer", bcm6318_timer_init);
+IRQCHIP_DECLARE(bcm6345_l2_timer, "brcm,bcm6345-timer", bcm6345_timer_init);
-- 
2.1.4

-- 
Simon Arlott

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

* [PATCH (v2) 2/10] MIPS: bmips: Add bcm6345-l2-timer interrupt controller
@ 2015-11-23 18:57       ` Simon Arlott
  0 siblings, 0 replies; 95+ messages in thread
From: Simon Arlott @ 2015-11-23 18:57 UTC (permalink / raw)
  To: Jonas Gorski
  Cc: devicetree-u79uwXL29TY76Z2rM5mHXA, Ralf Baechle, Thomas Gleixner,
	Jason Cooper, Marc Zyngier, Kevin Cernekee, Florian Fainelli,
	Wim Van Sebroeck, Miguel Gaio, Maxime Bizon,
	Linux Kernel Mailing List, MIPS Mailing List,
	linux-watchdog-u79uwXL29TY76Z2rM5mHXA, Rob Herring, Pawel Moll,
	Mark Rutland, Ian Campbell, Kumar Gala

Add the BCM6345/BCM6318 timer as an interrupt controller so that it can be
used by the watchdog to warn that its timer will expire soon.

Support for clocksource/clockevents is not implemented as the timer
interrupt is not per CPU (except on the BCM6318) and the MIPS clock is
better. This could be added later if required without changing the device
tree binding.

Signed-off-by: Simon Arlott <simon-A6De1vDTPLDsq35pWSNszA@public.gmane.org>
---
On 23/11/15 15:33, Jonas Gorski wrote:
> > On Sat, Nov 21, 2015 at 8:02 PM, Simon Arlott <simon-A6De1vDTPLDsq35pWSNszA@public.gmane.org> wrote:
>> >> +- compatible: should be "brcm,bcm<soc>-timer", "brcm,bcm6345-timer"
> > 
> > Since bcm6318 uses a slightly different register layout than the
> > earlier SoCs, I'd argue that using bcm6345-timer as a compatible for
> > bcm6318 is wrong.

I've updated the driver so that it outputs "BCM6318" instead of
"BCM6345" depending on which compatible string was matched.

Patches 1/4 and 2/4 are replaced with (v2) 1/10 and (v2) 2/10.

 drivers/irqchip/Kconfig                |   5 +
 drivers/irqchip/Makefile               |   1 +
 drivers/irqchip/irq-bcm6345-l2-timer.c | 321 +++++++++++++++++++++++++++++++++
 3 files changed, 327 insertions(+)
 create mode 100644 drivers/irqchip/irq-bcm6345-l2-timer.c

diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
index d307bb3..21c3d9b 100644
--- a/drivers/irqchip/Kconfig
+++ b/drivers/irqchip/Kconfig
@@ -70,6 +70,11 @@ config BCM6345_L1_IRQ
 	select GENERIC_IRQ_CHIP
 	select IRQ_DOMAIN
 
+config BCM6345_L2_TIMER_IRQ
+	bool
+	select GENERIC_IRQ_CHIP
+	select IRQ_DOMAIN
+
 config BCM7038_L1_IRQ
 	bool
 	select GENERIC_IRQ_CHIP
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index ded59cf..2687dea 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -44,6 +44,7 @@ obj-$(CONFIG_XTENSA_MX)			+= irq-xtensa-mx.o
 obj-$(CONFIG_IRQ_CROSSBAR)		+= irq-crossbar.o
 obj-$(CONFIG_SOC_VF610)			+= irq-vf610-mscm-ir.o
 obj-$(CONFIG_BCM6345_L1_IRQ)		+= irq-bcm6345-l1.o
+obj-$(CONFIG_BCM6345_L2_TIMER_IRQ)	+= irq-bcm6345-l2-timer.o
 obj-$(CONFIG_BCM7038_L1_IRQ)		+= irq-bcm7038-l1.o
 obj-$(CONFIG_BCM7120_L2_IRQ)		+= irq-bcm7120-l2.o
 obj-$(CONFIG_BRCMSTB_L2_IRQ)		+= irq-brcmstb-l2.o
diff --git a/drivers/irqchip/irq-bcm6345-l2-timer.c b/drivers/irqchip/irq-bcm6345-l2-timer.c
new file mode 100644
index 0000000..b7854f0
--- /dev/null
+++ b/drivers/irqchip/irq-bcm6345-l2-timer.c
@@ -0,0 +1,321 @@
+/*
+ * Copyright 2015 Simon Arlott
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * Based on arch/mips/bcm63xx/timer.c:
+ * Copyright (C) 2008 Maxime Bizon <mbizon-MmRyKUhfbQ9GWvitb5QawA@public.gmane.org>
+ *
+ * Registers for SoCs with 4 timers: BCM6345, BCM6328, BCM6362, BCM6816,
+ *                                   BCM68220,BCM63168, BCM63268
+ *   0x02: IRQ enable (u8)
+ *   0x03: IRQ status (u8)
+ *   0x04: Timer 0 control
+ *   0x08: Timer 1 control
+ *   0x0c: Timer 2 control
+ *   0x10: Timer 0 count
+ *   0x14: Timer 1 count
+ *   0x18: Timer 2 count
+ *   0x1c+: Watchdog registers
+ *
+ * Registers for SoCs with 5 timers: BCM6318
+ *   0x00: IRQ enable (u32)
+ *   0x04: IRQ status (u32)
+ *   0x08: Timer 0 control
+ *   0x0c: Timer 1 control
+ *   0x10: Timer 2 control
+ *   0x14: Timer 3 control
+ *   0x18: Timer 0 count
+ *   0x1c: Timer 1 count
+ *   0x20: Timer 2 count
+ *   0x24: Timer 3 count
+ *   0x28+: Watchdog registers
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/bitops.h>
+#include <linux/interrupt.h>
+#include <linux/irqreturn.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/of_platform.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/string.h>
+#include <linux/irqchip.h>
+#include <linux/irqchip/chained_irq.h>
+
+#define REG_6345_IRQ_ENABLE		0x02
+#define REG_6345_IRQ_STATUS		0x03
+#define REG_6345_CFG_BASE		0x04
+
+#define REG_6318_IRQ_ENABLE		0x00
+#define REG_6318_IRQ_STATUS		0x04
+#define REG_6318_CFG_BASE		0x08
+
+#define NR_TIMERS_6345			4
+#define WDT_TIMER_ID_6345		(NR_TIMERS_6345 - 1)
+
+#define NR_TIMERS_6318			5
+#define WDT_TIMER_ID_6318		(NR_TIMERS_6318 - 1)
+
+/* Per-timer count register */
+#define COUNT_MASK			(0x3fffffff)
+
+/* Per-timer control register */
+#define CONTROL_COUNTDOWN_MASK		(0x3fffffff)
+#define CONTROL_RSTCNTCLR_MASK		(1 << 30)
+#define CONTROL_ENABLE_MASK		(1 << 31)
+
+enum bcm6345_timer_type {
+	TIMER_TYPE_6345,
+	TIMER_TYPE_6318,
+};
+
+struct bcm6345_timer {
+	raw_spinlock_t lock;
+	void __iomem *base;
+	unsigned int irq;
+	struct irq_domain *domain;
+
+	enum bcm6345_timer_type type;
+	unsigned int nr_timers;
+	/* The watchdog timer has separate control/remaining registers
+	 * and cannot be masked.
+	 */
+	int wdt_timer_id;
+};
+
+static inline u32 bcm6345_timer_read_int_status(struct bcm6345_timer *timer)
+{
+	if (timer->type == TIMER_TYPE_6318)
+		return __raw_readl(timer->base + REG_6318_IRQ_STATUS);
+	else
+		return __raw_readb(timer->base + REG_6345_IRQ_STATUS);
+}
+
+static inline void bcm6345_timer_write_int_status(struct bcm6345_timer *timer,
+	u32 val)
+{
+	if (timer->type == TIMER_TYPE_6318)
+		__raw_writel(val, timer->base + REG_6318_IRQ_STATUS);
+	else
+		__raw_writeb(val, timer->base + REG_6345_IRQ_STATUS);
+}
+
+static inline u32 bcm6345_timer_read_int_enable(struct bcm6345_timer *timer)
+{
+	if (timer->type == TIMER_TYPE_6318)
+		return __raw_readl(timer->base + REG_6318_IRQ_ENABLE);
+	else
+		return __raw_readb(timer->base + REG_6345_IRQ_ENABLE);
+}
+
+static inline void bcm6345_timer_write_int_enable(struct bcm6345_timer *timer,
+	u32 val)
+{
+	if (timer->type == TIMER_TYPE_6318)
+		__raw_writel(val, timer->base + REG_6318_IRQ_ENABLE);
+	else
+		__raw_writeb(val, timer->base + REG_6345_IRQ_ENABLE);
+}
+
+static inline void bcm6345_timer_write_control(struct bcm6345_timer *timer,
+	unsigned int id, u32 val)
+{
+	if (timer->type == TIMER_TYPE_6318)
+		__raw_writel(0, timer->base + REG_6318_CFG_BASE + id * 4);
+	else
+		__raw_writel(0, timer->base + REG_6345_CFG_BASE + id * 4);
+}
+
+static inline void bcm6345_timer_write_count(struct bcm6345_timer *timer,
+	unsigned int id, u32 val)
+{
+	/* Count registers are immediately after the control registers */
+	return bcm6345_timer_write_control(timer, timer->nr_timers + id, val);
+}
+
+static inline void bcm6345_timer_stop(struct bcm6345_timer *timer, int id)
+{
+	if (id != timer->wdt_timer_id) {
+		bcm6345_timer_write_control(timer, id, 0);
+		bcm6345_timer_write_count(timer, id, 0);
+		bcm6345_timer_write_int_status(timer, BIT(id));
+	}
+}
+
+static void bcm6345_timer_interrupt(struct irq_desc *desc)
+{
+	struct bcm6345_timer *timer = irq_desc_get_handler_data(desc);
+	struct irq_chip *chip = irq_desc_get_chip(desc);
+	unsigned long pending;
+	irq_hw_number_t hwirq;
+	unsigned int irq;
+
+	chained_irq_enter(chip, desc);
+
+	pending = bcm6345_timer_read_int_status(timer);
+	pending &= bcm6345_timer_read_int_enable(timer) |
+			BIT(timer->wdt_timer_id); /* Watchdog can't be masked */
+
+	for_each_set_bit(hwirq, &pending, timer->nr_timers) {
+		irq = irq_linear_revmap(timer->domain, hwirq);
+		if (irq)
+			do_IRQ(irq);
+		else
+			spurious_interrupt();
+	}
+
+	chained_irq_exit(chip, desc);
+}
+
+static void bcm6345_timer_unmask(struct irq_data *d)
+{
+	struct bcm6345_timer *timer = irq_data_get_irq_chip_data(d);
+	unsigned long flags;
+	u8 val;
+
+	if (d->hwirq != timer->wdt_timer_id) {
+		raw_spin_lock_irqsave(&timer->lock, flags);
+		val = bcm6345_timer_read_int_enable(timer);
+		val |= BIT(d->hwirq);
+		bcm6345_timer_write_int_enable(timer, val);
+		raw_spin_unlock_irqrestore(&timer->lock, flags);
+	}
+}
+
+static void bcm6345_timer_mask(struct irq_data *d)
+{
+	struct bcm6345_timer *timer = irq_data_get_irq_chip_data(d);
+	unsigned long flags;
+	u32 val;
+
+	if (d->hwirq != timer->wdt_timer_id) {
+		raw_spin_lock_irqsave(&timer->lock, flags);
+		val = bcm6345_timer_read_int_enable(timer);
+		val &= ~BIT(d->hwirq);
+		bcm6345_timer_write_int_enable(timer, val);
+		raw_spin_unlock_irqrestore(&timer->lock, flags);
+	}
+}
+
+static void bcm6345_timer_eoi(struct irq_data *d)
+{
+	struct bcm6345_timer *timer = irq_data_get_irq_chip_data(d);
+
+	if (d->hwirq != timer->wdt_timer_id)
+		bcm6345_timer_write_int_status(timer, BIT(d->hwirq));
+}
+
+static struct irq_chip bcm6345_timer_chip = {
+	.name			= "bcm6345-timer",
+	.irq_mask		= bcm6345_timer_mask,
+	.irq_unmask		= bcm6345_timer_unmask,
+	.irq_eoi		= bcm6345_timer_eoi,
+};
+
+static int bcm6345_timer_map(struct irq_domain *d, unsigned int virq,
+			     irq_hw_number_t hwirq)
+{
+	struct bcm6345_timer *timer = d->host_data;
+
+	irq_set_chip_and_handler(virq, &bcm6345_timer_chip,
+		hwirq == timer->wdt_timer_id ?
+			handle_simple_irq : handle_fasteoi_irq);
+	irq_set_chip_data(virq, timer);
+	return 0;
+}
+
+static const struct irq_domain_ops bcm6345_timer_domain_ops = {
+	.xlate			= irq_domain_xlate_onecell,
+	.map			= bcm6345_timer_map,
+};
+
+static int __init bcm63xx_timer_init(struct device_node *node,
+	enum bcm6345_timer_type type, unsigned int nr_timers, int wdt_timer_id)
+{
+	struct bcm6345_timer *timer;
+	int ret, i;
+
+	timer = kzalloc(sizeof(*timer), GFP_KERNEL);
+	if (!timer)
+		return -ENOMEM;
+
+	raw_spin_lock_init(&timer->lock);
+	timer->type = type;
+	timer->nr_timers = nr_timers;
+	timer->wdt_timer_id = wdt_timer_id;
+
+	timer->irq = irq_of_parse_and_map(node, 0);
+	if (!timer->irq) {
+		pr_err("unable to map parent IRQ\n");
+		ret = -EINVAL;
+		goto free_timer;
+	}
+
+	timer->base = of_iomap(node, 0);
+	if (!timer->base) {
+		pr_err("unable to remap registers\n");
+		ret = -ENOMEM;
+		goto free_timer;
+	}
+
+	timer->domain = irq_domain_add_linear(node, timer->nr_timers,
+					&bcm6345_timer_domain_ops, timer);
+	if (!timer->domain) {
+		pr_err("unable to add IRQ domain");
+		ret = -ENOMEM;
+		goto unmap_io;
+	}
+
+	/* Mask all interrupts and stop all timers */
+	bcm6345_timer_write_int_enable(timer, 0);
+	for (i = 0; i < timer->nr_timers; i++)
+		bcm6345_timer_stop(timer, i);
+
+	irq_set_chained_handler_and_data(timer->irq,
+					bcm6345_timer_interrupt, timer);
+
+	if (timer->type == TIMER_TYPE_6318)
+		pr_info("registered BCM6318 L2 (timer) intc at MMIO 0x%p (irq = %d, IRQs: %d)\n",
+				timer->base, timer->irq, timer->nr_timers);
+	else
+		pr_info("registered BCM6345 L2 (timer) intc at MMIO 0x%p (irq = %d, IRQs: %d)\n",
+				timer->base, timer->irq, timer->nr_timers);
+	return 0;
+
+unmap_io:
+	iounmap(timer->base);
+free_timer:
+	kfree(timer);
+	return ret;
+}
+
+static int __init bcm6318_timer_init(struct device_node *node,
+				      struct device_node *parent)
+{
+	return bcm63xx_timer_init(node, TIMER_TYPE_6318,
+				NR_TIMERS_6318, WDT_TIMER_ID_6318);
+}
+
+static int __init bcm6345_timer_init(struct device_node *node,
+				      struct device_node *parent)
+{
+	return bcm63xx_timer_init(node, TIMER_TYPE_6345,
+				NR_TIMERS_6345, WDT_TIMER_ID_6345);
+}
+
+IRQCHIP_DECLARE(bcm6318_l2_timer, "brcm,bcm6318-timer", bcm6318_timer_init);
+IRQCHIP_DECLARE(bcm6345_l2_timer, "brcm,bcm6345-timer", bcm6345_timer_init);
-- 
2.1.4

-- 
Simon Arlott
--
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] 95+ messages in thread

* Re: [PATCH 6/10] watchdog: bcm63xx_wdt: Obtain watchdog clock HZ from "periph" clk
@ 2015-11-23 19:00                   ` Simon Arlott
  0 siblings, 0 replies; 95+ messages in thread
From: Simon Arlott @ 2015-11-23 19:00 UTC (permalink / raw)
  To: Florian Fainelli, Jonas Gorski
  Cc: Guenter Roeck, devicetree, Ralf Baechle, Thomas Gleixner,
	Jason Cooper, Marc Zyngier, Kevin Cernekee, Wim Van Sebroeck,
	Maxime Bizon, Linux Kernel Mailing List, MIPS Mailing List,
	linux-watchdog, Rob Herring, Pawel Moll, Mark Rutland,
	Ian Campbell, Kumar Gala

On 23/11/15 18:19, Florian Fainelli wrote:
> On 23/11/15 07:02, Jonas Gorski wrote:
>> On Sun, Nov 22, 2015 at 3:07 PM, Simon Arlott <simon@fire.lp0.eu> wrote:
>>> -#define WDT_HZ                 50000000                /* Fclk */
>>> +#define WDT_CLK_NAME           "periph"
>> 
>> @Florian:
>> Is this correct? The comment for the watchdog in 6358_map_part.h and
>> earlier claims that the clock is 40 MHz there, but the code uses 50MHz
>> - is this a bug in the comments or is it a bug taken over from the
>> original broadcom code? I'm sure that the periph clock being 50 MHz
>> even on the older chips is correct, else we'd have noticed that in
>> serial output (where it's also used).
> 
> There are references to a Fbus2 clock in documentation, but I could not
> find any actual documentation for its actual clock frequency, I would be
> surprised if this chip would have diverged from the previous and future
> ones and used a 40Mhz clock. 6345 started with a peripheral clock
> running at 50Mhz, and that is true for all chips since then AFAICT.
> 
> I agree we would have noticed this with the UART or SPI controllers if
> that was not true, so probably a code glitch here...

I've tested both the timer and the watchdog and they give near perfect
time intervals (within 1-2ms based on printk times over serial) so it'd
be obvious if they were out by 25%.

-- 
Simon Arlott

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

* Re: [PATCH 6/10] watchdog: bcm63xx_wdt: Obtain watchdog clock HZ from "periph" clk
@ 2015-11-23 19:00                   ` Simon Arlott
  0 siblings, 0 replies; 95+ messages in thread
From: Simon Arlott @ 2015-11-23 19:00 UTC (permalink / raw)
  To: Florian Fainelli, Jonas Gorski
  Cc: Guenter Roeck, devicetree-u79uwXL29TY76Z2rM5mHXA, Ralf Baechle,
	Thomas Gleixner, Jason Cooper, Marc Zyngier, Kevin Cernekee,
	Wim Van Sebroeck, Maxime Bizon, Linux Kernel Mailing List,
	MIPS Mailing List, linux-watchdog-u79uwXL29TY76Z2rM5mHXA,
	Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala

On 23/11/15 18:19, Florian Fainelli wrote:
> On 23/11/15 07:02, Jonas Gorski wrote:
>> On Sun, Nov 22, 2015 at 3:07 PM, Simon Arlott <simon-A6De1vDTPLDsq35pWSNszA@public.gmane.org> wrote:
>>> -#define WDT_HZ                 50000000                /* Fclk */
>>> +#define WDT_CLK_NAME           "periph"
>> 
>> @Florian:
>> Is this correct? The comment for the watchdog in 6358_map_part.h and
>> earlier claims that the clock is 40 MHz there, but the code uses 50MHz
>> - is this a bug in the comments or is it a bug taken over from the
>> original broadcom code? I'm sure that the periph clock being 50 MHz
>> even on the older chips is correct, else we'd have noticed that in
>> serial output (where it's also used).
> 
> There are references to a Fbus2 clock in documentation, but I could not
> find any actual documentation for its actual clock frequency, I would be
> surprised if this chip would have diverged from the previous and future
> ones and used a 40Mhz clock. 6345 started with a peripheral clock
> running at 50Mhz, and that is true for all chips since then AFAICT.
> 
> I agree we would have noticed this with the UART or SPI controllers if
> that was not true, so probably a code glitch here...

I've tested both the timer and the watchdog and they give near perfect
time intervals (within 1-2ms based on printk times over serial) so it'd
be obvious if they were out by 25%.

-- 
Simon Arlott
--
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] 95+ messages in thread

* Re: [PATCH 4/10] watchdog: bcm63xx_wdt: Handle hardware interrupt and remove software timer
@ 2015-11-24 18:21               ` Guenter Roeck
  0 siblings, 0 replies; 95+ messages in thread
From: Guenter Roeck @ 2015-11-24 18:21 UTC (permalink / raw)
  To: Simon Arlott, Florian Fainelli
  Cc: devicetree, Ralf Baechle, Thomas Gleixner, Jason Cooper,
	Marc Zyngier, Kevin Cernekee, Florian Fainelli, Wim Van Sebroeck,
	Maxime Bizon, Linux Kernel Mailing List, linux-mips,
	linux-watchdog, Rob Herring, Pawel Moll, Mark Rutland,
	Ian Campbell, Kumar Gala, Jonas Gorski

On Sun, Nov 22, 2015 at 02:05:16PM +0000, Simon Arlott wrote:
> There is a level triggered interrupt for the watchdog timer as part of
> the bcm63xx_timer device. The interrupt occurs when the hardware watchdog
> timer reaches 50% of the remaining time.
> 
> It is not possible to mask the interrupt within the bcm63xx_timer device.
> To get around this limitation, handle the interrupt by restarting the
> watchdog with the current remaining time (which will be half the previous
> timeout) so that the interrupt occurs again at 1/4th, 1/8th, etc. of the
> original timeout value until the watchdog forces a reboot.
> 
> The software timer was restarting the hardware watchdog with a 85 second
> timeout until the software timer expired, and then causing a panic()
> about 42.5 seconds later when the hardware interrupt occurred. The
> hardware watchdog would not reboot until a further 42.5 seconds had
> passed.
> 
> Remove the software timer and rely on the hardware timer directly,
> reducing the maximum timeout from 256 seconds to 85 seconds
> (2^32 / WDT_HZ).
> 

Florian,

can you have a look into this patch and confirm that there is no better
way to clear the interrupt status ?

Thanks,
Guenter

> Signed-off-by: Simon Arlott <simon@fire.lp0.eu>
> ---
>  drivers/watchdog/bcm63xx_wdt.c | 124 ++++++++++++++++++++++++-----------------
>  1 file changed, 72 insertions(+), 52 deletions(-)
> 
> diff --git a/drivers/watchdog/bcm63xx_wdt.c b/drivers/watchdog/bcm63xx_wdt.c
> index ab26fd9..f88fc97 100644
> --- a/drivers/watchdog/bcm63xx_wdt.c
> +++ b/drivers/watchdog/bcm63xx_wdt.c
> @@ -3,6 +3,7 @@
>   *
>   *  Copyright (C) 2007, Miguel Gaio <miguel.gaio@efixo.com>
>   *  Copyright (C) 2008, Florian Fainelli <florian@openwrt.org>
> + *  Copyright 2015 Simon Arlott
>   *
>   *  This program is free software; you can redistribute it and/or
>   *  modify it under the terms of the GNU General Public License
> @@ -20,11 +21,10 @@
>  #include <linux/miscdevice.h>
>  #include <linux/module.h>
>  #include <linux/moduleparam.h>
> +#include <linux/spinlock.h>
>  #include <linux/types.h>
>  #include <linux/uaccess.h>
>  #include <linux/watchdog.h>
> -#include <linux/timer.h>
> -#include <linux/jiffies.h>
>  #include <linux/interrupt.h>
>  #include <linux/ptrace.h>
>  #include <linux/resource.h>
> @@ -37,16 +37,17 @@
>  
>  #define PFX KBUILD_MODNAME
>  
> -#define WDT_HZ		50000000 /* Fclk */
> -#define WDT_DEFAULT_TIME	30      /* seconds */
> -#define WDT_MAX_TIME		256     /* seconds */
> +#define WDT_HZ			50000000		/* Fclk */
> +#define WDT_DEFAULT_TIME	30			/* seconds */
> +#define WDT_MAX_TIME		(0xffffffff / WDT_HZ)	/* seconds */
>  
> -static struct {
> +struct bcm63xx_wdt_hw {
> +	raw_spinlock_t lock;
>  	void __iomem *regs;
> -	struct timer_list timer;
>  	unsigned long inuse;
> -	atomic_t ticks;
> -} bcm63xx_wdt_device;
> +	bool running;
> +};
> +static struct bcm63xx_wdt_hw bcm63xx_wdt_device;
>  
>  static int expect_close;
>  
> @@ -59,48 +60,67 @@ MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
>  /* HW functions */
>  static void bcm63xx_wdt_hw_start(void)
>  {
> -	bcm_writel(0xfffffffe, bcm63xx_wdt_device.regs + WDT_DEFVAL_REG);
> +	unsigned long flags;
> +
> +	raw_spin_lock_irqsave(&bcm63xx_wdt_device.lock, flags);
> +	bcm_writel(wdt_time * WDT_HZ, bcm63xx_wdt_device.regs + WDT_DEFVAL_REG);
>  	bcm_writel(WDT_START_1, bcm63xx_wdt_device.regs + WDT_CTL_REG);
>  	bcm_writel(WDT_START_2, bcm63xx_wdt_device.regs + WDT_CTL_REG);
> +	bcm63xx_wdt_device.running = true;
> +	raw_spin_unlock_irqrestore(&bcm63xx_wdt_device.lock, flags);
>  }
>  
>  static void bcm63xx_wdt_hw_stop(void)
>  {
> +	unsigned long flags;
> +
> +	raw_spin_lock_irqsave(&bcm63xx_wdt_device.lock, flags);
>  	bcm_writel(WDT_STOP_1, bcm63xx_wdt_device.regs + WDT_CTL_REG);
>  	bcm_writel(WDT_STOP_2, bcm63xx_wdt_device.regs + WDT_CTL_REG);
> +	bcm63xx_wdt_device.running = false;
> +	raw_spin_unlock_irqrestore(&bcm63xx_wdt_device.lock, flags);
>  }
>  
> +/* The watchdog interrupt occurs when half the timeout is remaining */
>  static void bcm63xx_wdt_isr(void *data)
>  {
> -	struct pt_regs *regs = get_irq_regs();
> -
> -	die(PFX " fire", regs);
> -}
> -
> -static void bcm63xx_timer_tick(unsigned long unused)
> -{
> -	if (!atomic_dec_and_test(&bcm63xx_wdt_device.ticks)) {
> -		bcm63xx_wdt_hw_start();
> -		mod_timer(&bcm63xx_wdt_device.timer, jiffies + HZ);
> -	} else
> -		pr_crit("watchdog will restart system\n");
> -}
> -
> -static void bcm63xx_wdt_pet(void)
> -{
> -	atomic_set(&bcm63xx_wdt_device.ticks, wdt_time);
> -}
> -
> -static void bcm63xx_wdt_start(void)
> -{
> -	bcm63xx_wdt_pet();
> -	bcm63xx_timer_tick(0);
> -}
> +	struct bcm63xx_wdt_hw *hw = &bcm63xx_wdt_device;
> +	unsigned long flags;
> +
> +	raw_spin_lock_irqsave(&hw->lock, flags);
> +	if (!hw->running) {
> +		/* Stop the watchdog as it shouldn't be running */
> +		bcm_writel(WDT_STOP_1, hw->regs + WDT_CTL_REG);
> +		bcm_writel(WDT_STOP_2, hw->regs + WDT_CTL_REG);
> +	} else {
> +		u32 timeleft = bcm_readl(hw->regs + WDT_CTL_REG);
> +		u32 ms;
> +
> +		if (timeleft >= 2) {
> +			/* The only way to clear this level triggered interrupt
> +			 * without disrupting the normal running of the watchdog
> +			 * is to restart the watchdog with the current remaining
> +			 * time value (which will be half the previous timeout)
> +			 * so the interrupt occurs again at 1/4th, 1/8th, etc.
> +			 * of the original timeout value until we reboot.
> +			 *
> +			 * This is done with a lock held in case userspace is
> +			 * trying to restart the watchdog on another CPU.
> +			 */
> +			bcm_writel(timeleft, hw->regs + WDT_DEFVAL_REG);
> +			bcm_writel(WDT_START_1, hw->regs + WDT_CTL_REG);
> +			bcm_writel(WDT_START_2, hw->regs + WDT_CTL_REG);
> +		} else {
> +			/* The watchdog cannot be started with a time of less
> +			 * than 2 ticks (it won't fire).
> +			 */
> +			die(PFX ": watchdog timer expired\n", get_irq_regs());
> +		}
>  
> -static void bcm63xx_wdt_pause(void)
> -{
> -	del_timer_sync(&bcm63xx_wdt_device.timer);
> -	bcm63xx_wdt_hw_stop();
> +		ms = timeleft / (WDT_HZ / 1000);
> +		pr_alert("warning timer fired, reboot in %ums\n", ms);
> +	}
> +	raw_spin_unlock_irqrestore(&hw->lock, flags);
>  }
>  
>  static int bcm63xx_wdt_settimeout(int new_time)
> @@ -118,17 +138,17 @@ static int bcm63xx_wdt_open(struct inode *inode, struct file *file)
>  	if (test_and_set_bit(0, &bcm63xx_wdt_device.inuse))
>  		return -EBUSY;
>  
> -	bcm63xx_wdt_start();
> +	bcm63xx_wdt_hw_start();
>  	return nonseekable_open(inode, file);
>  }
>  
>  static int bcm63xx_wdt_release(struct inode *inode, struct file *file)
>  {
>  	if (expect_close == 42)
> -		bcm63xx_wdt_pause();
> +		bcm63xx_wdt_hw_stop();
>  	else {
>  		pr_crit("Unexpected close, not stopping watchdog!\n");
> -		bcm63xx_wdt_start();
> +		bcm63xx_wdt_hw_start();
>  	}
>  	clear_bit(0, &bcm63xx_wdt_device.inuse);
>  	expect_close = 0;
> @@ -153,7 +173,7 @@ static ssize_t bcm63xx_wdt_write(struct file *file, const char *data,
>  					expect_close = 42;
>  			}
>  		}
> -		bcm63xx_wdt_pet();
> +		bcm63xx_wdt_hw_start();
>  	}
>  	return len;
>  }
> @@ -187,18 +207,18 @@ static long bcm63xx_wdt_ioctl(struct file *file, unsigned int cmd,
>  			return -EFAULT;
>  
>  		if (new_value & WDIOS_DISABLECARD) {
> -			bcm63xx_wdt_pause();
> +			bcm63xx_wdt_hw_stop();
>  			retval = 0;
>  		}
>  		if (new_value & WDIOS_ENABLECARD) {
> -			bcm63xx_wdt_start();
> +			bcm63xx_wdt_hw_start();
>  			retval = 0;
>  		}
>  
>  		return retval;
>  
>  	case WDIOC_KEEPALIVE:
> -		bcm63xx_wdt_pet();
> +		bcm63xx_wdt_hw_start();
>  		return 0;
>  
>  	case WDIOC_SETTIMEOUT:
> @@ -208,7 +228,7 @@ static long bcm63xx_wdt_ioctl(struct file *file, unsigned int cmd,
>  		if (bcm63xx_wdt_settimeout(new_value))
>  			return -EINVAL;
>  
> -		bcm63xx_wdt_pet();
> +		bcm63xx_wdt_hw_start();
>  
>  	case WDIOC_GETTIMEOUT:
>  		return put_user(wdt_time, p);
> @@ -240,8 +260,6 @@ static int bcm63xx_wdt_probe(struct platform_device *pdev)
>  	int ret;
>  	struct resource *r;
>  
> -	setup_timer(&bcm63xx_wdt_device.timer, bcm63xx_timer_tick, 0L);
> -
>  	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>  	if (!r) {
>  		dev_err(&pdev->dev, "failed to get resources\n");
> @@ -255,6 +273,8 @@ static int bcm63xx_wdt_probe(struct platform_device *pdev)
>  		return -ENXIO;
>  	}
>  
> +	raw_spin_lock_init(&bcm63xx_wdt_device.lock);
> +
>  	ret = bcm63xx_timer_register(TIMER_WDT_ID, bcm63xx_wdt_isr, NULL);
>  	if (ret < 0) {
>  		dev_err(&pdev->dev, "failed to register wdt timer isr\n");
> @@ -264,8 +284,8 @@ static int bcm63xx_wdt_probe(struct platform_device *pdev)
>  	if (bcm63xx_wdt_settimeout(wdt_time)) {
>  		bcm63xx_wdt_settimeout(WDT_DEFAULT_TIME);
>  		dev_info(&pdev->dev,
> -			": wdt_time value must be 1 <= wdt_time <= 256, using %d\n",
> -			wdt_time);
> +			": wdt_time value must be 1 <= wdt_time <= %d, using %d\n",
> +			WDT_MAX_TIME, wdt_time);
>  	}
>  
>  	ret = misc_register(&bcm63xx_wdt_miscdev);
> @@ -287,7 +307,7 @@ unregister_timer:
>  static int bcm63xx_wdt_remove(struct platform_device *pdev)
>  {
>  	if (!nowayout)
> -		bcm63xx_wdt_pause();
> +		bcm63xx_wdt_hw_stop();
>  
>  	misc_deregister(&bcm63xx_wdt_miscdev);
>  	bcm63xx_timer_unregister(TIMER_WDT_ID);
> @@ -296,7 +316,7 @@ static int bcm63xx_wdt_remove(struct platform_device *pdev)
>  
>  static void bcm63xx_wdt_shutdown(struct platform_device *pdev)
>  {
> -	bcm63xx_wdt_pause();
> +	bcm63xx_wdt_hw_stop();
>  }
>  
>  static struct platform_driver bcm63xx_wdt_driver = {
> -- 
> 2.1.4
> 
> -- 
> Simon Arlott

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

* Re: [PATCH 4/10] watchdog: bcm63xx_wdt: Handle hardware interrupt and remove software timer
@ 2015-11-24 18:21               ` Guenter Roeck
  0 siblings, 0 replies; 95+ messages in thread
From: Guenter Roeck @ 2015-11-24 18:21 UTC (permalink / raw)
  To: Simon Arlott
  Cc: devicetree-u79uwXL29TY76Z2rM5mHXA, Ralf Baechle, Thomas Gleixner,
	Jason Cooper, Marc Zyngier, Kevin Cernekee, Florian Fainelli,
	Wim Van Sebroeck, Maxime Bizon, Linux Kernel Mailing List,
	linux-mips-6z/3iImG2C8G8FEW9MqTrA,
	linux-watchdog-u79uwXL29TY76Z2rM5mHXA, Rob Herring, Pawel Moll,
	Mark Rutland, Ian Campbell, Kumar Gala, Jonas Gorski

On Sun, Nov 22, 2015 at 02:05:16PM +0000, Simon Arlott wrote:
> There is a level triggered interrupt for the watchdog timer as part of
> the bcm63xx_timer device. The interrupt occurs when the hardware watchdog
> timer reaches 50% of the remaining time.
> 
> It is not possible to mask the interrupt within the bcm63xx_timer device.
> To get around this limitation, handle the interrupt by restarting the
> watchdog with the current remaining time (which will be half the previous
> timeout) so that the interrupt occurs again at 1/4th, 1/8th, etc. of the
> original timeout value until the watchdog forces a reboot.
> 
> The software timer was restarting the hardware watchdog with a 85 second
> timeout until the software timer expired, and then causing a panic()
> about 42.5 seconds later when the hardware interrupt occurred. The
> hardware watchdog would not reboot until a further 42.5 seconds had
> passed.
> 
> Remove the software timer and rely on the hardware timer directly,
> reducing the maximum timeout from 256 seconds to 85 seconds
> (2^32 / WDT_HZ).
> 

Florian,

can you have a look into this patch and confirm that there is no better
way to clear the interrupt status ?

Thanks,
Guenter

> Signed-off-by: Simon Arlott <simon-A6De1vDTPLDsq35pWSNszA@public.gmane.org>
> ---
>  drivers/watchdog/bcm63xx_wdt.c | 124 ++++++++++++++++++++++++-----------------
>  1 file changed, 72 insertions(+), 52 deletions(-)
> 
> diff --git a/drivers/watchdog/bcm63xx_wdt.c b/drivers/watchdog/bcm63xx_wdt.c
> index ab26fd9..f88fc97 100644
> --- a/drivers/watchdog/bcm63xx_wdt.c
> +++ b/drivers/watchdog/bcm63xx_wdt.c
> @@ -3,6 +3,7 @@
>   *
>   *  Copyright (C) 2007, Miguel Gaio <miguel.gaio-HH44TBFINEIAvxtiuMwx3w@public.gmane.org>
>   *  Copyright (C) 2008, Florian Fainelli <florian-p3rKhJxN3npAfugRpC6u6w@public.gmane.org>
> + *  Copyright 2015 Simon Arlott
>   *
>   *  This program is free software; you can redistribute it and/or
>   *  modify it under the terms of the GNU General Public License
> @@ -20,11 +21,10 @@
>  #include <linux/miscdevice.h>
>  #include <linux/module.h>
>  #include <linux/moduleparam.h>
> +#include <linux/spinlock.h>
>  #include <linux/types.h>
>  #include <linux/uaccess.h>
>  #include <linux/watchdog.h>
> -#include <linux/timer.h>
> -#include <linux/jiffies.h>
>  #include <linux/interrupt.h>
>  #include <linux/ptrace.h>
>  #include <linux/resource.h>
> @@ -37,16 +37,17 @@
>  
>  #define PFX KBUILD_MODNAME
>  
> -#define WDT_HZ		50000000 /* Fclk */
> -#define WDT_DEFAULT_TIME	30      /* seconds */
> -#define WDT_MAX_TIME		256     /* seconds */
> +#define WDT_HZ			50000000		/* Fclk */
> +#define WDT_DEFAULT_TIME	30			/* seconds */
> +#define WDT_MAX_TIME		(0xffffffff / WDT_HZ)	/* seconds */
>  
> -static struct {
> +struct bcm63xx_wdt_hw {
> +	raw_spinlock_t lock;
>  	void __iomem *regs;
> -	struct timer_list timer;
>  	unsigned long inuse;
> -	atomic_t ticks;
> -} bcm63xx_wdt_device;
> +	bool running;
> +};
> +static struct bcm63xx_wdt_hw bcm63xx_wdt_device;
>  
>  static int expect_close;
>  
> @@ -59,48 +60,67 @@ MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
>  /* HW functions */
>  static void bcm63xx_wdt_hw_start(void)
>  {
> -	bcm_writel(0xfffffffe, bcm63xx_wdt_device.regs + WDT_DEFVAL_REG);
> +	unsigned long flags;
> +
> +	raw_spin_lock_irqsave(&bcm63xx_wdt_device.lock, flags);
> +	bcm_writel(wdt_time * WDT_HZ, bcm63xx_wdt_device.regs + WDT_DEFVAL_REG);
>  	bcm_writel(WDT_START_1, bcm63xx_wdt_device.regs + WDT_CTL_REG);
>  	bcm_writel(WDT_START_2, bcm63xx_wdt_device.regs + WDT_CTL_REG);
> +	bcm63xx_wdt_device.running = true;
> +	raw_spin_unlock_irqrestore(&bcm63xx_wdt_device.lock, flags);
>  }
>  
>  static void bcm63xx_wdt_hw_stop(void)
>  {
> +	unsigned long flags;
> +
> +	raw_spin_lock_irqsave(&bcm63xx_wdt_device.lock, flags);
>  	bcm_writel(WDT_STOP_1, bcm63xx_wdt_device.regs + WDT_CTL_REG);
>  	bcm_writel(WDT_STOP_2, bcm63xx_wdt_device.regs + WDT_CTL_REG);
> +	bcm63xx_wdt_device.running = false;
> +	raw_spin_unlock_irqrestore(&bcm63xx_wdt_device.lock, flags);
>  }
>  
> +/* The watchdog interrupt occurs when half the timeout is remaining */
>  static void bcm63xx_wdt_isr(void *data)
>  {
> -	struct pt_regs *regs = get_irq_regs();
> -
> -	die(PFX " fire", regs);
> -}
> -
> -static void bcm63xx_timer_tick(unsigned long unused)
> -{
> -	if (!atomic_dec_and_test(&bcm63xx_wdt_device.ticks)) {
> -		bcm63xx_wdt_hw_start();
> -		mod_timer(&bcm63xx_wdt_device.timer, jiffies + HZ);
> -	} else
> -		pr_crit("watchdog will restart system\n");
> -}
> -
> -static void bcm63xx_wdt_pet(void)
> -{
> -	atomic_set(&bcm63xx_wdt_device.ticks, wdt_time);
> -}
> -
> -static void bcm63xx_wdt_start(void)
> -{
> -	bcm63xx_wdt_pet();
> -	bcm63xx_timer_tick(0);
> -}
> +	struct bcm63xx_wdt_hw *hw = &bcm63xx_wdt_device;
> +	unsigned long flags;
> +
> +	raw_spin_lock_irqsave(&hw->lock, flags);
> +	if (!hw->running) {
> +		/* Stop the watchdog as it shouldn't be running */
> +		bcm_writel(WDT_STOP_1, hw->regs + WDT_CTL_REG);
> +		bcm_writel(WDT_STOP_2, hw->regs + WDT_CTL_REG);
> +	} else {
> +		u32 timeleft = bcm_readl(hw->regs + WDT_CTL_REG);
> +		u32 ms;
> +
> +		if (timeleft >= 2) {
> +			/* The only way to clear this level triggered interrupt
> +			 * without disrupting the normal running of the watchdog
> +			 * is to restart the watchdog with the current remaining
> +			 * time value (which will be half the previous timeout)
> +			 * so the interrupt occurs again at 1/4th, 1/8th, etc.
> +			 * of the original timeout value until we reboot.
> +			 *
> +			 * This is done with a lock held in case userspace is
> +			 * trying to restart the watchdog on another CPU.
> +			 */
> +			bcm_writel(timeleft, hw->regs + WDT_DEFVAL_REG);
> +			bcm_writel(WDT_START_1, hw->regs + WDT_CTL_REG);
> +			bcm_writel(WDT_START_2, hw->regs + WDT_CTL_REG);
> +		} else {
> +			/* The watchdog cannot be started with a time of less
> +			 * than 2 ticks (it won't fire).
> +			 */
> +			die(PFX ": watchdog timer expired\n", get_irq_regs());
> +		}
>  
> -static void bcm63xx_wdt_pause(void)
> -{
> -	del_timer_sync(&bcm63xx_wdt_device.timer);
> -	bcm63xx_wdt_hw_stop();
> +		ms = timeleft / (WDT_HZ / 1000);
> +		pr_alert("warning timer fired, reboot in %ums\n", ms);
> +	}
> +	raw_spin_unlock_irqrestore(&hw->lock, flags);
>  }
>  
>  static int bcm63xx_wdt_settimeout(int new_time)
> @@ -118,17 +138,17 @@ static int bcm63xx_wdt_open(struct inode *inode, struct file *file)
>  	if (test_and_set_bit(0, &bcm63xx_wdt_device.inuse))
>  		return -EBUSY;
>  
> -	bcm63xx_wdt_start();
> +	bcm63xx_wdt_hw_start();
>  	return nonseekable_open(inode, file);
>  }
>  
>  static int bcm63xx_wdt_release(struct inode *inode, struct file *file)
>  {
>  	if (expect_close == 42)
> -		bcm63xx_wdt_pause();
> +		bcm63xx_wdt_hw_stop();
>  	else {
>  		pr_crit("Unexpected close, not stopping watchdog!\n");
> -		bcm63xx_wdt_start();
> +		bcm63xx_wdt_hw_start();
>  	}
>  	clear_bit(0, &bcm63xx_wdt_device.inuse);
>  	expect_close = 0;
> @@ -153,7 +173,7 @@ static ssize_t bcm63xx_wdt_write(struct file *file, const char *data,
>  					expect_close = 42;
>  			}
>  		}
> -		bcm63xx_wdt_pet();
> +		bcm63xx_wdt_hw_start();
>  	}
>  	return len;
>  }
> @@ -187,18 +207,18 @@ static long bcm63xx_wdt_ioctl(struct file *file, unsigned int cmd,
>  			return -EFAULT;
>  
>  		if (new_value & WDIOS_DISABLECARD) {
> -			bcm63xx_wdt_pause();
> +			bcm63xx_wdt_hw_stop();
>  			retval = 0;
>  		}
>  		if (new_value & WDIOS_ENABLECARD) {
> -			bcm63xx_wdt_start();
> +			bcm63xx_wdt_hw_start();
>  			retval = 0;
>  		}
>  
>  		return retval;
>  
>  	case WDIOC_KEEPALIVE:
> -		bcm63xx_wdt_pet();
> +		bcm63xx_wdt_hw_start();
>  		return 0;
>  
>  	case WDIOC_SETTIMEOUT:
> @@ -208,7 +228,7 @@ static long bcm63xx_wdt_ioctl(struct file *file, unsigned int cmd,
>  		if (bcm63xx_wdt_settimeout(new_value))
>  			return -EINVAL;
>  
> -		bcm63xx_wdt_pet();
> +		bcm63xx_wdt_hw_start();
>  
>  	case WDIOC_GETTIMEOUT:
>  		return put_user(wdt_time, p);
> @@ -240,8 +260,6 @@ static int bcm63xx_wdt_probe(struct platform_device *pdev)
>  	int ret;
>  	struct resource *r;
>  
> -	setup_timer(&bcm63xx_wdt_device.timer, bcm63xx_timer_tick, 0L);
> -
>  	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>  	if (!r) {
>  		dev_err(&pdev->dev, "failed to get resources\n");
> @@ -255,6 +273,8 @@ static int bcm63xx_wdt_probe(struct platform_device *pdev)
>  		return -ENXIO;
>  	}
>  
> +	raw_spin_lock_init(&bcm63xx_wdt_device.lock);
> +
>  	ret = bcm63xx_timer_register(TIMER_WDT_ID, bcm63xx_wdt_isr, NULL);
>  	if (ret < 0) {
>  		dev_err(&pdev->dev, "failed to register wdt timer isr\n");
> @@ -264,8 +284,8 @@ static int bcm63xx_wdt_probe(struct platform_device *pdev)
>  	if (bcm63xx_wdt_settimeout(wdt_time)) {
>  		bcm63xx_wdt_settimeout(WDT_DEFAULT_TIME);
>  		dev_info(&pdev->dev,
> -			": wdt_time value must be 1 <= wdt_time <= 256, using %d\n",
> -			wdt_time);
> +			": wdt_time value must be 1 <= wdt_time <= %d, using %d\n",
> +			WDT_MAX_TIME, wdt_time);
>  	}
>  
>  	ret = misc_register(&bcm63xx_wdt_miscdev);
> @@ -287,7 +307,7 @@ unregister_timer:
>  static int bcm63xx_wdt_remove(struct platform_device *pdev)
>  {
>  	if (!nowayout)
> -		bcm63xx_wdt_pause();
> +		bcm63xx_wdt_hw_stop();
>  
>  	misc_deregister(&bcm63xx_wdt_miscdev);
>  	bcm63xx_timer_unregister(TIMER_WDT_ID);
> @@ -296,7 +316,7 @@ static int bcm63xx_wdt_remove(struct platform_device *pdev)
>  
>  static void bcm63xx_wdt_shutdown(struct platform_device *pdev)
>  {
> -	bcm63xx_wdt_pause();
> +	bcm63xx_wdt_hw_stop();
>  }
>  
>  static struct platform_driver bcm63xx_wdt_driver = {
> -- 
> 2.1.4
> 
> -- 
> Simon Arlott
--
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] 95+ messages in thread

* Re: [PATCH 4/10] watchdog: bcm63xx_wdt: Handle hardware interrupt and remove software timer
@ 2015-11-24 18:21               ` Guenter Roeck
  0 siblings, 0 replies; 95+ messages in thread
From: Guenter Roeck @ 2015-11-24 18:21 UTC (permalink / raw)
  To: Simon Arlott, Florian Fainelli
  Cc: devicetree, Ralf Baechle, Thomas Gleixner, Jason Cooper,
	Marc Zyngier, Kevin Cernekee, Wim Van Sebroeck, Maxime Bizon,
	Linux Kernel Mailing List, linux-mips, linux-watchdog,
	Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
	Jonas Gorski

On Sun, Nov 22, 2015 at 02:05:16PM +0000, Simon Arlott wrote:
> There is a level triggered interrupt for the watchdog timer as part of
> the bcm63xx_timer device. The interrupt occurs when the hardware watchdog
> timer reaches 50% of the remaining time.
> 
> It is not possible to mask the interrupt within the bcm63xx_timer device.
> To get around this limitation, handle the interrupt by restarting the
> watchdog with the current remaining time (which will be half the previous
> timeout) so that the interrupt occurs again at 1/4th, 1/8th, etc. of the
> original timeout value until the watchdog forces a reboot.
> 
> The software timer was restarting the hardware watchdog with a 85 second
> timeout until the software timer expired, and then causing a panic()
> about 42.5 seconds later when the hardware interrupt occurred. The
> hardware watchdog would not reboot until a further 42.5 seconds had
> passed.
> 
> Remove the software timer and rely on the hardware timer directly,
> reducing the maximum timeout from 256 seconds to 85 seconds
> (2^32 / WDT_HZ).
> 

Florian,

can you have a look into this patch and confirm that there is no better
way to clear the interrupt status ?

Thanks,
Guenter

> Signed-off-by: Simon Arlott <simon@fire.lp0.eu>
> ---
>  drivers/watchdog/bcm63xx_wdt.c | 124 ++++++++++++++++++++++++-----------------
>  1 file changed, 72 insertions(+), 52 deletions(-)
> 
> diff --git a/drivers/watchdog/bcm63xx_wdt.c b/drivers/watchdog/bcm63xx_wdt.c
> index ab26fd9..f88fc97 100644
> --- a/drivers/watchdog/bcm63xx_wdt.c
> +++ b/drivers/watchdog/bcm63xx_wdt.c
> @@ -3,6 +3,7 @@
>   *
>   *  Copyright (C) 2007, Miguel Gaio <miguel.gaio@efixo.com>
>   *  Copyright (C) 2008, Florian Fainelli <florian@openwrt.org>
> + *  Copyright 2015 Simon Arlott
>   *
>   *  This program is free software; you can redistribute it and/or
>   *  modify it under the terms of the GNU General Public License
> @@ -20,11 +21,10 @@
>  #include <linux/miscdevice.h>
>  #include <linux/module.h>
>  #include <linux/moduleparam.h>
> +#include <linux/spinlock.h>
>  #include <linux/types.h>
>  #include <linux/uaccess.h>
>  #include <linux/watchdog.h>
> -#include <linux/timer.h>
> -#include <linux/jiffies.h>
>  #include <linux/interrupt.h>
>  #include <linux/ptrace.h>
>  #include <linux/resource.h>
> @@ -37,16 +37,17 @@
>  
>  #define PFX KBUILD_MODNAME
>  
> -#define WDT_HZ		50000000 /* Fclk */
> -#define WDT_DEFAULT_TIME	30      /* seconds */
> -#define WDT_MAX_TIME		256     /* seconds */
> +#define WDT_HZ			50000000		/* Fclk */
> +#define WDT_DEFAULT_TIME	30			/* seconds */
> +#define WDT_MAX_TIME		(0xffffffff / WDT_HZ)	/* seconds */
>  
> -static struct {
> +struct bcm63xx_wdt_hw {
> +	raw_spinlock_t lock;
>  	void __iomem *regs;
> -	struct timer_list timer;
>  	unsigned long inuse;
> -	atomic_t ticks;
> -} bcm63xx_wdt_device;
> +	bool running;
> +};
> +static struct bcm63xx_wdt_hw bcm63xx_wdt_device;
>  
>  static int expect_close;
>  
> @@ -59,48 +60,67 @@ MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
>  /* HW functions */
>  static void bcm63xx_wdt_hw_start(void)
>  {
> -	bcm_writel(0xfffffffe, bcm63xx_wdt_device.regs + WDT_DEFVAL_REG);
> +	unsigned long flags;
> +
> +	raw_spin_lock_irqsave(&bcm63xx_wdt_device.lock, flags);
> +	bcm_writel(wdt_time * WDT_HZ, bcm63xx_wdt_device.regs + WDT_DEFVAL_REG);
>  	bcm_writel(WDT_START_1, bcm63xx_wdt_device.regs + WDT_CTL_REG);
>  	bcm_writel(WDT_START_2, bcm63xx_wdt_device.regs + WDT_CTL_REG);
> +	bcm63xx_wdt_device.running = true;
> +	raw_spin_unlock_irqrestore(&bcm63xx_wdt_device.lock, flags);
>  }
>  
>  static void bcm63xx_wdt_hw_stop(void)
>  {
> +	unsigned long flags;
> +
> +	raw_spin_lock_irqsave(&bcm63xx_wdt_device.lock, flags);
>  	bcm_writel(WDT_STOP_1, bcm63xx_wdt_device.regs + WDT_CTL_REG);
>  	bcm_writel(WDT_STOP_2, bcm63xx_wdt_device.regs + WDT_CTL_REG);
> +	bcm63xx_wdt_device.running = false;
> +	raw_spin_unlock_irqrestore(&bcm63xx_wdt_device.lock, flags);
>  }
>  
> +/* The watchdog interrupt occurs when half the timeout is remaining */
>  static void bcm63xx_wdt_isr(void *data)
>  {
> -	struct pt_regs *regs = get_irq_regs();
> -
> -	die(PFX " fire", regs);
> -}
> -
> -static void bcm63xx_timer_tick(unsigned long unused)
> -{
> -	if (!atomic_dec_and_test(&bcm63xx_wdt_device.ticks)) {
> -		bcm63xx_wdt_hw_start();
> -		mod_timer(&bcm63xx_wdt_device.timer, jiffies + HZ);
> -	} else
> -		pr_crit("watchdog will restart system\n");
> -}
> -
> -static void bcm63xx_wdt_pet(void)
> -{
> -	atomic_set(&bcm63xx_wdt_device.ticks, wdt_time);
> -}
> -
> -static void bcm63xx_wdt_start(void)
> -{
> -	bcm63xx_wdt_pet();
> -	bcm63xx_timer_tick(0);
> -}
> +	struct bcm63xx_wdt_hw *hw = &bcm63xx_wdt_device;
> +	unsigned long flags;
> +
> +	raw_spin_lock_irqsave(&hw->lock, flags);
> +	if (!hw->running) {
> +		/* Stop the watchdog as it shouldn't be running */
> +		bcm_writel(WDT_STOP_1, hw->regs + WDT_CTL_REG);
> +		bcm_writel(WDT_STOP_2, hw->regs + WDT_CTL_REG);
> +	} else {
> +		u32 timeleft = bcm_readl(hw->regs + WDT_CTL_REG);
> +		u32 ms;
> +
> +		if (timeleft >= 2) {
> +			/* The only way to clear this level triggered interrupt
> +			 * without disrupting the normal running of the watchdog
> +			 * is to restart the watchdog with the current remaining
> +			 * time value (which will be half the previous timeout)
> +			 * so the interrupt occurs again at 1/4th, 1/8th, etc.
> +			 * of the original timeout value until we reboot.
> +			 *
> +			 * This is done with a lock held in case userspace is
> +			 * trying to restart the watchdog on another CPU.
> +			 */
> +			bcm_writel(timeleft, hw->regs + WDT_DEFVAL_REG);
> +			bcm_writel(WDT_START_1, hw->regs + WDT_CTL_REG);
> +			bcm_writel(WDT_START_2, hw->regs + WDT_CTL_REG);
> +		} else {
> +			/* The watchdog cannot be started with a time of less
> +			 * than 2 ticks (it won't fire).
> +			 */
> +			die(PFX ": watchdog timer expired\n", get_irq_regs());
> +		}
>  
> -static void bcm63xx_wdt_pause(void)
> -{
> -	del_timer_sync(&bcm63xx_wdt_device.timer);
> -	bcm63xx_wdt_hw_stop();
> +		ms = timeleft / (WDT_HZ / 1000);
> +		pr_alert("warning timer fired, reboot in %ums\n", ms);
> +	}
> +	raw_spin_unlock_irqrestore(&hw->lock, flags);
>  }
>  
>  static int bcm63xx_wdt_settimeout(int new_time)
> @@ -118,17 +138,17 @@ static int bcm63xx_wdt_open(struct inode *inode, struct file *file)
>  	if (test_and_set_bit(0, &bcm63xx_wdt_device.inuse))
>  		return -EBUSY;
>  
> -	bcm63xx_wdt_start();
> +	bcm63xx_wdt_hw_start();
>  	return nonseekable_open(inode, file);
>  }
>  
>  static int bcm63xx_wdt_release(struct inode *inode, struct file *file)
>  {
>  	if (expect_close == 42)
> -		bcm63xx_wdt_pause();
> +		bcm63xx_wdt_hw_stop();
>  	else {
>  		pr_crit("Unexpected close, not stopping watchdog!\n");
> -		bcm63xx_wdt_start();
> +		bcm63xx_wdt_hw_start();
>  	}
>  	clear_bit(0, &bcm63xx_wdt_device.inuse);
>  	expect_close = 0;
> @@ -153,7 +173,7 @@ static ssize_t bcm63xx_wdt_write(struct file *file, const char *data,
>  					expect_close = 42;
>  			}
>  		}
> -		bcm63xx_wdt_pet();
> +		bcm63xx_wdt_hw_start();
>  	}
>  	return len;
>  }
> @@ -187,18 +207,18 @@ static long bcm63xx_wdt_ioctl(struct file *file, unsigned int cmd,
>  			return -EFAULT;
>  
>  		if (new_value & WDIOS_DISABLECARD) {
> -			bcm63xx_wdt_pause();
> +			bcm63xx_wdt_hw_stop();
>  			retval = 0;
>  		}
>  		if (new_value & WDIOS_ENABLECARD) {
> -			bcm63xx_wdt_start();
> +			bcm63xx_wdt_hw_start();
>  			retval = 0;
>  		}
>  
>  		return retval;
>  
>  	case WDIOC_KEEPALIVE:
> -		bcm63xx_wdt_pet();
> +		bcm63xx_wdt_hw_start();
>  		return 0;
>  
>  	case WDIOC_SETTIMEOUT:
> @@ -208,7 +228,7 @@ static long bcm63xx_wdt_ioctl(struct file *file, unsigned int cmd,
>  		if (bcm63xx_wdt_settimeout(new_value))
>  			return -EINVAL;
>  
> -		bcm63xx_wdt_pet();
> +		bcm63xx_wdt_hw_start();
>  
>  	case WDIOC_GETTIMEOUT:
>  		return put_user(wdt_time, p);
> @@ -240,8 +260,6 @@ static int bcm63xx_wdt_probe(struct platform_device *pdev)
>  	int ret;
>  	struct resource *r;
>  
> -	setup_timer(&bcm63xx_wdt_device.timer, bcm63xx_timer_tick, 0L);
> -
>  	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>  	if (!r) {
>  		dev_err(&pdev->dev, "failed to get resources\n");
> @@ -255,6 +273,8 @@ static int bcm63xx_wdt_probe(struct platform_device *pdev)
>  		return -ENXIO;
>  	}
>  
> +	raw_spin_lock_init(&bcm63xx_wdt_device.lock);
> +
>  	ret = bcm63xx_timer_register(TIMER_WDT_ID, bcm63xx_wdt_isr, NULL);
>  	if (ret < 0) {
>  		dev_err(&pdev->dev, "failed to register wdt timer isr\n");
> @@ -264,8 +284,8 @@ static int bcm63xx_wdt_probe(struct platform_device *pdev)
>  	if (bcm63xx_wdt_settimeout(wdt_time)) {
>  		bcm63xx_wdt_settimeout(WDT_DEFAULT_TIME);
>  		dev_info(&pdev->dev,
> -			": wdt_time value must be 1 <= wdt_time <= 256, using %d\n",
> -			wdt_time);
> +			": wdt_time value must be 1 <= wdt_time <= %d, using %d\n",
> +			WDT_MAX_TIME, wdt_time);
>  	}
>  
>  	ret = misc_register(&bcm63xx_wdt_miscdev);
> @@ -287,7 +307,7 @@ unregister_timer:
>  static int bcm63xx_wdt_remove(struct platform_device *pdev)
>  {
>  	if (!nowayout)
> -		bcm63xx_wdt_pause();
> +		bcm63xx_wdt_hw_stop();
>  
>  	misc_deregister(&bcm63xx_wdt_miscdev);
>  	bcm63xx_timer_unregister(TIMER_WDT_ID);
> @@ -296,7 +316,7 @@ static int bcm63xx_wdt_remove(struct platform_device *pdev)
>  
>  static void bcm63xx_wdt_shutdown(struct platform_device *pdev)
>  {
> -	bcm63xx_wdt_pause();
> +	bcm63xx_wdt_hw_stop();
>  }
>  
>  static struct platform_driver bcm63xx_wdt_driver = {
> -- 
> 2.1.4
> 
> -- 
> Simon Arlott

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

* [PATCH (v3) 2/10] MIPS: bmips: Add bcm6345-l2-timer interrupt controller
@ 2015-11-24 22:10         ` Simon Arlott
  0 siblings, 0 replies; 95+ messages in thread
From: Simon Arlott @ 2015-11-24 22:10 UTC (permalink / raw)
  To: MIPS Mailing List
  Cc: Jonas Gorski, devicetree, Ralf Baechle, Thomas Gleixner,
	Jason Cooper, Marc Zyngier, Kevin Cernekee, Florian Fainelli,
	Wim Van Sebroeck, Miguel Gaio, Maxime Bizon,
	Linux Kernel Mailing List, linux-watchdog, Rob Herring,
	Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala

Add the BCM6345/BCM6318 timer as an interrupt controller so that it can be
used by the watchdog to warn that its timer will expire soon.

Support for clocksource/clockevents is not implemented as the timer
interrupt is not per CPU (except on the BCM6318) and the MIPS clock is
better. This could be added later if required without changing the device
tree binding.

Signed-off-by: Simon Arlott <simon@fire.lp0.eu>
---
Fixed the offset of the count registers, they were writing off by one which
caused it to set the watchdog timeout to 0.

 drivers/irqchip/Kconfig                |   5 +
 drivers/irqchip/Makefile               |   1 +
 drivers/irqchip/irq-bcm6345-l2-timer.c | 324 +++++++++++++++++++++++++++++++++
 3 files changed, 330 insertions(+)
 create mode 100644 drivers/irqchip/irq-bcm6345-l2-timer.c

diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
index d307bb3..21c3d9b 100644
--- a/drivers/irqchip/Kconfig
+++ b/drivers/irqchip/Kconfig
@@ -70,6 +70,11 @@ config BCM6345_L1_IRQ
 	select GENERIC_IRQ_CHIP
 	select IRQ_DOMAIN

+config BCM6345_L2_TIMER_IRQ
+	bool
+	select GENERIC_IRQ_CHIP
+	select IRQ_DOMAIN
+
 config BCM7038_L1_IRQ
 	bool
 	select GENERIC_IRQ_CHIP
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index ded59cf..2687dea 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -44,6 +44,7 @@ obj-$(CONFIG_XTENSA_MX)			+= irq-xtensa-mx.o
 obj-$(CONFIG_IRQ_CROSSBAR)		+= irq-crossbar.o
 obj-$(CONFIG_SOC_VF610)			+= irq-vf610-mscm-ir.o
 obj-$(CONFIG_BCM6345_L1_IRQ)		+= irq-bcm6345-l1.o
+obj-$(CONFIG_BCM6345_L2_TIMER_IRQ)	+= irq-bcm6345-l2-timer.o
 obj-$(CONFIG_BCM7038_L1_IRQ)		+= irq-bcm7038-l1.o
 obj-$(CONFIG_BCM7120_L2_IRQ)		+= irq-bcm7120-l2.o
 obj-$(CONFIG_BRCMSTB_L2_IRQ)		+= irq-brcmstb-l2.o
diff --git a/drivers/irqchip/irq-bcm6345-l2-timer.c b/drivers/irqchip/irq-bcm6345-l2-timer.c
new file mode 100644
index 0000000..4e6f71b
--- /dev/null
+++ b/drivers/irqchip/irq-bcm6345-l2-timer.c
@@ -0,0 +1,324 @@
+/*
+ * Copyright 2015 Simon Arlott
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * Based on arch/mips/bcm63xx/timer.c:
+ * Copyright (C) 2008 Maxime Bizon <mbizon@freebox.fr>
+ *
+ * Registers for SoCs with 4 timers: BCM6345, BCM6328, BCM6362, BCM6816,
+ *                                   BCM68220,BCM63168, BCM63268
+ *   0x02: IRQ enable (u8)
+ *   0x03: IRQ status (u8)
+ *   0x04: Timer 0 control
+ *   0x08: Timer 1 control
+ *   0x0c: Timer 2 control
+ *   0x10: Timer 0 count
+ *   0x14: Timer 1 count
+ *   0x18: Timer 2 count
+ *   0x1c+: Watchdog registers
+ *
+ * Registers for SoCs with 5 timers: BCM6318
+ *   0x00: IRQ enable (u32)
+ *   0x04: IRQ status (u32)
+ *   0x08: Timer 0 control
+ *   0x0c: Timer 1 control
+ *   0x10: Timer 2 control
+ *   0x14: Timer 3 control
+ *   0x18: Timer 0 count
+ *   0x1c: Timer 1 count
+ *   0x20: Timer 2 count
+ *   0x24: Timer 3 count
+ *   0x28+: Watchdog registers
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/bitops.h>
+#include <linux/interrupt.h>
+#include <linux/irqreturn.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/of_platform.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/string.h>
+#include <linux/irqchip.h>
+#include <linux/irqchip/chained_irq.h>
+
+#define REG_6345_IRQ_ENABLE		0x02
+#define REG_6345_IRQ_STATUS		0x03
+#define REG_6345_CONTROL_BASE		0x04
+#define REG_6345_COUNT_BASE		0x10
+
+#define REG_6318_IRQ_ENABLE		0x00
+#define REG_6318_IRQ_STATUS		0x04
+#define REG_6318_CONTROL_BASE		0x08
+#define REG_6318_COUNT_BASE		0x18
+
+#define NR_TIMERS_6345			4
+#define WDT_TIMER_ID_6345		(NR_TIMERS_6345 - 1)
+
+#define NR_TIMERS_6318			5
+#define WDT_TIMER_ID_6318		(NR_TIMERS_6318 - 1)
+
+/* Per-timer count register */
+#define COUNT_MASK			(0x3fffffff)
+
+/* Per-timer control register */
+#define CONTROL_COUNTDOWN_MASK		(0x3fffffff)
+#define CONTROL_RSTCNTCLR_MASK		(1 << 30)
+#define CONTROL_ENABLE_MASK		(1 << 31)
+
+enum bcm6345_timer_type {
+	TIMER_TYPE_6345,
+	TIMER_TYPE_6318,
+};
+
+struct bcm6345_timer {
+	raw_spinlock_t lock;
+	void __iomem *base;
+	unsigned int irq;
+	struct irq_domain *domain;
+
+	enum bcm6345_timer_type type;
+	unsigned int nr_timers;
+	/* The watchdog timer has separate control/remaining registers
+	 * and cannot be masked.
+	 */
+	int wdt_timer_id;
+};
+
+static inline u32 bcm6345_timer_read_int_status(struct bcm6345_timer *timer)
+{
+	if (timer->type == TIMER_TYPE_6318)
+		return __raw_readl(timer->base + REG_6318_IRQ_STATUS);
+	else
+		return __raw_readb(timer->base + REG_6345_IRQ_STATUS);
+}
+
+static inline void bcm6345_timer_write_int_status(struct bcm6345_timer *timer,
+	u32 val)
+{
+	if (timer->type == TIMER_TYPE_6318)
+		__raw_writel(val, timer->base + REG_6318_IRQ_STATUS);
+	else
+		__raw_writeb(val, timer->base + REG_6345_IRQ_STATUS);
+}
+
+static inline u32 bcm6345_timer_read_int_enable(struct bcm6345_timer *timer)
+{
+	if (timer->type == TIMER_TYPE_6318)
+		return __raw_readl(timer->base + REG_6318_IRQ_ENABLE);
+	else
+		return __raw_readb(timer->base + REG_6345_IRQ_ENABLE);
+}
+
+static inline void bcm6345_timer_write_int_enable(struct bcm6345_timer *timer,
+	u32 val)
+{
+	if (timer->type == TIMER_TYPE_6318)
+		__raw_writel(val, timer->base + REG_6318_IRQ_ENABLE);
+	else
+		__raw_writeb(val, timer->base + REG_6345_IRQ_ENABLE);
+}
+
+static inline void bcm6345_timer_write_control(struct bcm6345_timer *timer,
+	unsigned int id, u32 val)
+{
+	if (timer->type == TIMER_TYPE_6318)
+		__raw_writel(0, timer->base + REG_6318_CONTROL_BASE + id * 4);
+	else
+		__raw_writel(0, timer->base + REG_6345_CONTROL_BASE + id * 4);
+}
+
+static inline void bcm6345_timer_write_count(struct bcm6345_timer *timer,
+	unsigned int id, u32 val)
+{
+	if (timer->type == TIMER_TYPE_6318)
+		__raw_writel(0, timer->base + REG_6318_COUNT_BASE + id * 4);
+	else
+		__raw_writel(0, timer->base + REG_6345_COUNT_BASE + id * 4);
+}
+
+static inline void bcm6345_timer_stop(struct bcm6345_timer *timer, int id)
+{
+	bcm6345_timer_write_control(timer, id, 0);
+	bcm6345_timer_write_count(timer, id, 0);
+	bcm6345_timer_write_int_status(timer, BIT(id));
+}
+
+static void bcm6345_timer_interrupt(struct irq_desc *desc)
+{
+	struct bcm6345_timer *timer = irq_desc_get_handler_data(desc);
+	struct irq_chip *chip = irq_desc_get_chip(desc);
+	unsigned long pending;
+	irq_hw_number_t hwirq;
+	unsigned int irq;
+
+	chained_irq_enter(chip, desc);
+
+	pending = bcm6345_timer_read_int_status(timer);
+	pending &= bcm6345_timer_read_int_enable(timer) |
+			BIT(timer->wdt_timer_id); /* Watchdog can't be masked */
+
+	for_each_set_bit(hwirq, &pending, timer->nr_timers) {
+		irq = irq_linear_revmap(timer->domain, hwirq);
+		if (irq)
+			do_IRQ(irq);
+		else
+			spurious_interrupt();
+	}
+
+	chained_irq_exit(chip, desc);
+}
+
+static void bcm6345_timer_unmask(struct irq_data *d)
+{
+	struct bcm6345_timer *timer = irq_data_get_irq_chip_data(d);
+	unsigned long flags;
+	u8 val;
+
+	if (d->hwirq != timer->wdt_timer_id) {
+		raw_spin_lock_irqsave(&timer->lock, flags);
+		val = bcm6345_timer_read_int_enable(timer);
+		val |= BIT(d->hwirq);
+		bcm6345_timer_write_int_enable(timer, val);
+		raw_spin_unlock_irqrestore(&timer->lock, flags);
+	}
+}
+
+static void bcm6345_timer_mask(struct irq_data *d)
+{
+	struct bcm6345_timer *timer = irq_data_get_irq_chip_data(d);
+	unsigned long flags;
+	u32 val;
+
+	if (d->hwirq != timer->wdt_timer_id) {
+		raw_spin_lock_irqsave(&timer->lock, flags);
+		val = bcm6345_timer_read_int_enable(timer);
+		val &= ~BIT(d->hwirq);
+		bcm6345_timer_write_int_enable(timer, val);
+		raw_spin_unlock_irqrestore(&timer->lock, flags);
+	}
+}
+
+static void bcm6345_timer_eoi(struct irq_data *d)
+{
+	struct bcm6345_timer *timer = irq_data_get_irq_chip_data(d);
+
+	if (d->hwirq != timer->wdt_timer_id)
+		bcm6345_timer_write_int_status(timer, BIT(d->hwirq));
+}
+
+static struct irq_chip bcm6345_timer_chip = {
+	.name			= "bcm6345-timer",
+	.irq_mask		= bcm6345_timer_mask,
+	.irq_unmask		= bcm6345_timer_unmask,
+	.irq_eoi		= bcm6345_timer_eoi,
+};
+
+static int bcm6345_timer_map(struct irq_domain *d, unsigned int virq,
+			     irq_hw_number_t hwirq)
+{
+	struct bcm6345_timer *timer = d->host_data;
+
+	irq_set_chip_and_handler(virq, &bcm6345_timer_chip,
+		hwirq == timer->wdt_timer_id ?
+			handle_simple_irq : handle_fasteoi_irq);
+	irq_set_chip_data(virq, timer);
+	return 0;
+}
+
+static const struct irq_domain_ops bcm6345_timer_domain_ops = {
+	.xlate			= irq_domain_xlate_onecell,
+	.map			= bcm6345_timer_map,
+};
+
+static int __init bcm63xx_timer_init(struct device_node *node,
+	enum bcm6345_timer_type type, unsigned int nr_timers, int wdt_timer_id)
+{
+	struct bcm6345_timer *timer;
+	int ret, i;
+
+	timer = kzalloc(sizeof(*timer), GFP_KERNEL);
+	if (!timer)
+		return -ENOMEM;
+
+	raw_spin_lock_init(&timer->lock);
+	timer->type = type;
+	timer->nr_timers = nr_timers;
+	timer->wdt_timer_id = wdt_timer_id;
+
+	timer->irq = irq_of_parse_and_map(node, 0);
+	if (!timer->irq) {
+		pr_err("unable to map parent IRQ\n");
+		ret = -EINVAL;
+		goto free_timer;
+	}
+
+	timer->base = of_iomap(node, 0);
+	if (!timer->base) {
+		pr_err("unable to remap registers\n");
+		ret = -ENOMEM;
+		goto free_timer;
+	}
+
+	timer->domain = irq_domain_add_linear(node, timer->nr_timers,
+					&bcm6345_timer_domain_ops, timer);
+	if (!timer->domain) {
+		pr_err("unable to add IRQ domain");
+		ret = -ENOMEM;
+		goto unmap_io;
+	}
+
+	/* Mask all interrupts and stop all timers */
+	bcm6345_timer_write_int_enable(timer, 0);
+	for (i = 0; i < timer->nr_timers; i++)
+		if (i != timer->wdt_timer_id)
+			bcm6345_timer_stop(timer, i);
+
+	irq_set_chained_handler_and_data(timer->irq,
+					bcm6345_timer_interrupt, timer);
+
+	if (timer->type == TIMER_TYPE_6318)
+		pr_info("registered BCM6318 L2 (timer) intc at MMIO 0x%p (irq = %d, IRQs: %d)\n",
+				timer->base, timer->irq, timer->nr_timers);
+	else
+		pr_info("registered BCM6345 L2 (timer) intc at MMIO 0x%p (irq = %d, IRQs: %d)\n",
+				timer->base, timer->irq, timer->nr_timers);
+	return 0;
+
+unmap_io:
+	iounmap(timer->base);
+free_timer:
+	kfree(timer);
+	return ret;
+}
+
+static int __init bcm6318_timer_init(struct device_node *node,
+				      struct device_node *parent)
+{
+	return bcm63xx_timer_init(node, TIMER_TYPE_6318,
+				NR_TIMERS_6318, WDT_TIMER_ID_6318);
+}
+
+static int __init bcm6345_timer_init(struct device_node *node,
+				      struct device_node *parent)
+{
+	return bcm63xx_timer_init(node, TIMER_TYPE_6345,
+				NR_TIMERS_6345, WDT_TIMER_ID_6345);
+}
+
+IRQCHIP_DECLARE(bcm6318_l2_timer, "brcm,bcm6318-timer", bcm6318_timer_init);
+IRQCHIP_DECLARE(bcm6345_l2_timer, "brcm,bcm6345-timer", bcm6345_timer_init);
-- 
2.1.4

-- 
Simon Arlott

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

* [PATCH (v3) 2/10] MIPS: bmips: Add bcm6345-l2-timer interrupt controller
@ 2015-11-24 22:10         ` Simon Arlott
  0 siblings, 0 replies; 95+ messages in thread
From: Simon Arlott @ 2015-11-24 22:10 UTC (permalink / raw)
  To: MIPS Mailing List
  Cc: Jonas Gorski, devicetree-u79uwXL29TY76Z2rM5mHXA, Ralf Baechle,
	Thomas Gleixner, Jason Cooper, Marc Zyngier, Kevin Cernekee,
	Florian Fainelli, Wim Van Sebroeck, Miguel Gaio, Maxime Bizon,
	Linux Kernel Mailing List, linux-watchdog-u79uwXL29TY76Z2rM5mHXA,
	Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala

Add the BCM6345/BCM6318 timer as an interrupt controller so that it can be
used by the watchdog to warn that its timer will expire soon.

Support for clocksource/clockevents is not implemented as the timer
interrupt is not per CPU (except on the BCM6318) and the MIPS clock is
better. This could be added later if required without changing the device
tree binding.

Signed-off-by: Simon Arlott <simon-A6De1vDTPLDsq35pWSNszA@public.gmane.org>
---
Fixed the offset of the count registers, they were writing off by one which
caused it to set the watchdog timeout to 0.

 drivers/irqchip/Kconfig                |   5 +
 drivers/irqchip/Makefile               |   1 +
 drivers/irqchip/irq-bcm6345-l2-timer.c | 324 +++++++++++++++++++++++++++++++++
 3 files changed, 330 insertions(+)
 create mode 100644 drivers/irqchip/irq-bcm6345-l2-timer.c

diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
index d307bb3..21c3d9b 100644
--- a/drivers/irqchip/Kconfig
+++ b/drivers/irqchip/Kconfig
@@ -70,6 +70,11 @@ config BCM6345_L1_IRQ
 	select GENERIC_IRQ_CHIP
 	select IRQ_DOMAIN

+config BCM6345_L2_TIMER_IRQ
+	bool
+	select GENERIC_IRQ_CHIP
+	select IRQ_DOMAIN
+
 config BCM7038_L1_IRQ
 	bool
 	select GENERIC_IRQ_CHIP
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index ded59cf..2687dea 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -44,6 +44,7 @@ obj-$(CONFIG_XTENSA_MX)			+= irq-xtensa-mx.o
 obj-$(CONFIG_IRQ_CROSSBAR)		+= irq-crossbar.o
 obj-$(CONFIG_SOC_VF610)			+= irq-vf610-mscm-ir.o
 obj-$(CONFIG_BCM6345_L1_IRQ)		+= irq-bcm6345-l1.o
+obj-$(CONFIG_BCM6345_L2_TIMER_IRQ)	+= irq-bcm6345-l2-timer.o
 obj-$(CONFIG_BCM7038_L1_IRQ)		+= irq-bcm7038-l1.o
 obj-$(CONFIG_BCM7120_L2_IRQ)		+= irq-bcm7120-l2.o
 obj-$(CONFIG_BRCMSTB_L2_IRQ)		+= irq-brcmstb-l2.o
diff --git a/drivers/irqchip/irq-bcm6345-l2-timer.c b/drivers/irqchip/irq-bcm6345-l2-timer.c
new file mode 100644
index 0000000..4e6f71b
--- /dev/null
+++ b/drivers/irqchip/irq-bcm6345-l2-timer.c
@@ -0,0 +1,324 @@
+/*
+ * Copyright 2015 Simon Arlott
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * Based on arch/mips/bcm63xx/timer.c:
+ * Copyright (C) 2008 Maxime Bizon <mbizon-MmRyKUhfbQ9GWvitb5QawA@public.gmane.org>
+ *
+ * Registers for SoCs with 4 timers: BCM6345, BCM6328, BCM6362, BCM6816,
+ *                                   BCM68220,BCM63168, BCM63268
+ *   0x02: IRQ enable (u8)
+ *   0x03: IRQ status (u8)
+ *   0x04: Timer 0 control
+ *   0x08: Timer 1 control
+ *   0x0c: Timer 2 control
+ *   0x10: Timer 0 count
+ *   0x14: Timer 1 count
+ *   0x18: Timer 2 count
+ *   0x1c+: Watchdog registers
+ *
+ * Registers for SoCs with 5 timers: BCM6318
+ *   0x00: IRQ enable (u32)
+ *   0x04: IRQ status (u32)
+ *   0x08: Timer 0 control
+ *   0x0c: Timer 1 control
+ *   0x10: Timer 2 control
+ *   0x14: Timer 3 control
+ *   0x18: Timer 0 count
+ *   0x1c: Timer 1 count
+ *   0x20: Timer 2 count
+ *   0x24: Timer 3 count
+ *   0x28+: Watchdog registers
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/bitops.h>
+#include <linux/interrupt.h>
+#include <linux/irqreturn.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/of_platform.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/string.h>
+#include <linux/irqchip.h>
+#include <linux/irqchip/chained_irq.h>
+
+#define REG_6345_IRQ_ENABLE		0x02
+#define REG_6345_IRQ_STATUS		0x03
+#define REG_6345_CONTROL_BASE		0x04
+#define REG_6345_COUNT_BASE		0x10
+
+#define REG_6318_IRQ_ENABLE		0x00
+#define REG_6318_IRQ_STATUS		0x04
+#define REG_6318_CONTROL_BASE		0x08
+#define REG_6318_COUNT_BASE		0x18
+
+#define NR_TIMERS_6345			4
+#define WDT_TIMER_ID_6345		(NR_TIMERS_6345 - 1)
+
+#define NR_TIMERS_6318			5
+#define WDT_TIMER_ID_6318		(NR_TIMERS_6318 - 1)
+
+/* Per-timer count register */
+#define COUNT_MASK			(0x3fffffff)
+
+/* Per-timer control register */
+#define CONTROL_COUNTDOWN_MASK		(0x3fffffff)
+#define CONTROL_RSTCNTCLR_MASK		(1 << 30)
+#define CONTROL_ENABLE_MASK		(1 << 31)
+
+enum bcm6345_timer_type {
+	TIMER_TYPE_6345,
+	TIMER_TYPE_6318,
+};
+
+struct bcm6345_timer {
+	raw_spinlock_t lock;
+	void __iomem *base;
+	unsigned int irq;
+	struct irq_domain *domain;
+
+	enum bcm6345_timer_type type;
+	unsigned int nr_timers;
+	/* The watchdog timer has separate control/remaining registers
+	 * and cannot be masked.
+	 */
+	int wdt_timer_id;
+};
+
+static inline u32 bcm6345_timer_read_int_status(struct bcm6345_timer *timer)
+{
+	if (timer->type == TIMER_TYPE_6318)
+		return __raw_readl(timer->base + REG_6318_IRQ_STATUS);
+	else
+		return __raw_readb(timer->base + REG_6345_IRQ_STATUS);
+}
+
+static inline void bcm6345_timer_write_int_status(struct bcm6345_timer *timer,
+	u32 val)
+{
+	if (timer->type == TIMER_TYPE_6318)
+		__raw_writel(val, timer->base + REG_6318_IRQ_STATUS);
+	else
+		__raw_writeb(val, timer->base + REG_6345_IRQ_STATUS);
+}
+
+static inline u32 bcm6345_timer_read_int_enable(struct bcm6345_timer *timer)
+{
+	if (timer->type == TIMER_TYPE_6318)
+		return __raw_readl(timer->base + REG_6318_IRQ_ENABLE);
+	else
+		return __raw_readb(timer->base + REG_6345_IRQ_ENABLE);
+}
+
+static inline void bcm6345_timer_write_int_enable(struct bcm6345_timer *timer,
+	u32 val)
+{
+	if (timer->type == TIMER_TYPE_6318)
+		__raw_writel(val, timer->base + REG_6318_IRQ_ENABLE);
+	else
+		__raw_writeb(val, timer->base + REG_6345_IRQ_ENABLE);
+}
+
+static inline void bcm6345_timer_write_control(struct bcm6345_timer *timer,
+	unsigned int id, u32 val)
+{
+	if (timer->type == TIMER_TYPE_6318)
+		__raw_writel(0, timer->base + REG_6318_CONTROL_BASE + id * 4);
+	else
+		__raw_writel(0, timer->base + REG_6345_CONTROL_BASE + id * 4);
+}
+
+static inline void bcm6345_timer_write_count(struct bcm6345_timer *timer,
+	unsigned int id, u32 val)
+{
+	if (timer->type == TIMER_TYPE_6318)
+		__raw_writel(0, timer->base + REG_6318_COUNT_BASE + id * 4);
+	else
+		__raw_writel(0, timer->base + REG_6345_COUNT_BASE + id * 4);
+}
+
+static inline void bcm6345_timer_stop(struct bcm6345_timer *timer, int id)
+{
+	bcm6345_timer_write_control(timer, id, 0);
+	bcm6345_timer_write_count(timer, id, 0);
+	bcm6345_timer_write_int_status(timer, BIT(id));
+}
+
+static void bcm6345_timer_interrupt(struct irq_desc *desc)
+{
+	struct bcm6345_timer *timer = irq_desc_get_handler_data(desc);
+	struct irq_chip *chip = irq_desc_get_chip(desc);
+	unsigned long pending;
+	irq_hw_number_t hwirq;
+	unsigned int irq;
+
+	chained_irq_enter(chip, desc);
+
+	pending = bcm6345_timer_read_int_status(timer);
+	pending &= bcm6345_timer_read_int_enable(timer) |
+			BIT(timer->wdt_timer_id); /* Watchdog can't be masked */
+
+	for_each_set_bit(hwirq, &pending, timer->nr_timers) {
+		irq = irq_linear_revmap(timer->domain, hwirq);
+		if (irq)
+			do_IRQ(irq);
+		else
+			spurious_interrupt();
+	}
+
+	chained_irq_exit(chip, desc);
+}
+
+static void bcm6345_timer_unmask(struct irq_data *d)
+{
+	struct bcm6345_timer *timer = irq_data_get_irq_chip_data(d);
+	unsigned long flags;
+	u8 val;
+
+	if (d->hwirq != timer->wdt_timer_id) {
+		raw_spin_lock_irqsave(&timer->lock, flags);
+		val = bcm6345_timer_read_int_enable(timer);
+		val |= BIT(d->hwirq);
+		bcm6345_timer_write_int_enable(timer, val);
+		raw_spin_unlock_irqrestore(&timer->lock, flags);
+	}
+}
+
+static void bcm6345_timer_mask(struct irq_data *d)
+{
+	struct bcm6345_timer *timer = irq_data_get_irq_chip_data(d);
+	unsigned long flags;
+	u32 val;
+
+	if (d->hwirq != timer->wdt_timer_id) {
+		raw_spin_lock_irqsave(&timer->lock, flags);
+		val = bcm6345_timer_read_int_enable(timer);
+		val &= ~BIT(d->hwirq);
+		bcm6345_timer_write_int_enable(timer, val);
+		raw_spin_unlock_irqrestore(&timer->lock, flags);
+	}
+}
+
+static void bcm6345_timer_eoi(struct irq_data *d)
+{
+	struct bcm6345_timer *timer = irq_data_get_irq_chip_data(d);
+
+	if (d->hwirq != timer->wdt_timer_id)
+		bcm6345_timer_write_int_status(timer, BIT(d->hwirq));
+}
+
+static struct irq_chip bcm6345_timer_chip = {
+	.name			= "bcm6345-timer",
+	.irq_mask		= bcm6345_timer_mask,
+	.irq_unmask		= bcm6345_timer_unmask,
+	.irq_eoi		= bcm6345_timer_eoi,
+};
+
+static int bcm6345_timer_map(struct irq_domain *d, unsigned int virq,
+			     irq_hw_number_t hwirq)
+{
+	struct bcm6345_timer *timer = d->host_data;
+
+	irq_set_chip_and_handler(virq, &bcm6345_timer_chip,
+		hwirq == timer->wdt_timer_id ?
+			handle_simple_irq : handle_fasteoi_irq);
+	irq_set_chip_data(virq, timer);
+	return 0;
+}
+
+static const struct irq_domain_ops bcm6345_timer_domain_ops = {
+	.xlate			= irq_domain_xlate_onecell,
+	.map			= bcm6345_timer_map,
+};
+
+static int __init bcm63xx_timer_init(struct device_node *node,
+	enum bcm6345_timer_type type, unsigned int nr_timers, int wdt_timer_id)
+{
+	struct bcm6345_timer *timer;
+	int ret, i;
+
+	timer = kzalloc(sizeof(*timer), GFP_KERNEL);
+	if (!timer)
+		return -ENOMEM;
+
+	raw_spin_lock_init(&timer->lock);
+	timer->type = type;
+	timer->nr_timers = nr_timers;
+	timer->wdt_timer_id = wdt_timer_id;
+
+	timer->irq = irq_of_parse_and_map(node, 0);
+	if (!timer->irq) {
+		pr_err("unable to map parent IRQ\n");
+		ret = -EINVAL;
+		goto free_timer;
+	}
+
+	timer->base = of_iomap(node, 0);
+	if (!timer->base) {
+		pr_err("unable to remap registers\n");
+		ret = -ENOMEM;
+		goto free_timer;
+	}
+
+	timer->domain = irq_domain_add_linear(node, timer->nr_timers,
+					&bcm6345_timer_domain_ops, timer);
+	if (!timer->domain) {
+		pr_err("unable to add IRQ domain");
+		ret = -ENOMEM;
+		goto unmap_io;
+	}
+
+	/* Mask all interrupts and stop all timers */
+	bcm6345_timer_write_int_enable(timer, 0);
+	for (i = 0; i < timer->nr_timers; i++)
+		if (i != timer->wdt_timer_id)
+			bcm6345_timer_stop(timer, i);
+
+	irq_set_chained_handler_and_data(timer->irq,
+					bcm6345_timer_interrupt, timer);
+
+	if (timer->type == TIMER_TYPE_6318)
+		pr_info("registered BCM6318 L2 (timer) intc at MMIO 0x%p (irq = %d, IRQs: %d)\n",
+				timer->base, timer->irq, timer->nr_timers);
+	else
+		pr_info("registered BCM6345 L2 (timer) intc at MMIO 0x%p (irq = %d, IRQs: %d)\n",
+				timer->base, timer->irq, timer->nr_timers);
+	return 0;
+
+unmap_io:
+	iounmap(timer->base);
+free_timer:
+	kfree(timer);
+	return ret;
+}
+
+static int __init bcm6318_timer_init(struct device_node *node,
+				      struct device_node *parent)
+{
+	return bcm63xx_timer_init(node, TIMER_TYPE_6318,
+				NR_TIMERS_6318, WDT_TIMER_ID_6318);
+}
+
+static int __init bcm6345_timer_init(struct device_node *node,
+				      struct device_node *parent)
+{
+	return bcm63xx_timer_init(node, TIMER_TYPE_6345,
+				NR_TIMERS_6345, WDT_TIMER_ID_6345);
+}
+
+IRQCHIP_DECLARE(bcm6318_l2_timer, "brcm,bcm6318-timer", bcm6318_timer_init);
+IRQCHIP_DECLARE(bcm6345_l2_timer, "brcm,bcm6345-timer", bcm6345_timer_init);
-- 
2.1.4

-- 
Simon Arlott
--
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] 95+ messages in thread

* [PATCH (v2) 6/10] watchdog: bcm63xx_wdt: Obtain watchdog clock HZ from "periph" clk
  2015-11-22 14:07             ` Simon Arlott
  (?)
  (?)
@ 2015-11-24 22:12             ` Simon Arlott
  2015-11-24 22:42                 ` Florian Fainelli
  -1 siblings, 1 reply; 95+ messages in thread
From: Simon Arlott @ 2015-11-24 22:12 UTC (permalink / raw)
  To: linux-watchdog
  Cc: Guenter Roeck, devicetree, Ralf Baechle, Thomas Gleixner,
	Jason Cooper, Marc Zyngier, Kevin Cernekee, Florian Fainelli,
	Wim Van Sebroeck, Maxime Bizon, Linux Kernel Mailing List,
	linux-mips, Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell,
	Kumar Gala, Jonas Gorski

Instead of using a fixed clock HZ in the driver, obtain it from the
"periph" clk that the watchdog timer uses.

Signed-off-by: Simon Arlott <simon@fire.lp0.eu>
---
Changed to check for -EPROBE_DEFER before printing an error.

 drivers/watchdog/bcm63xx_wdt.c | 38 ++++++++++++++++++++++++++++++++------
 1 file changed, 32 insertions(+), 6 deletions(-)

diff --git a/drivers/watchdog/bcm63xx_wdt.c b/drivers/watchdog/bcm63xx_wdt.c
index 1d2a501..3c7667a 100644
--- a/drivers/watchdog/bcm63xx_wdt.c
+++ b/drivers/watchdog/bcm63xx_wdt.c
@@ -13,6 +13,7 @@

 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt

+#include <linux/clk.h>
 #include <linux/errno.h>
 #include <linux/io.h>
 #include <linux/kernel.h>
@@ -32,11 +33,13 @@

 #define PFX KBUILD_MODNAME

-#define WDT_HZ			50000000		/* Fclk */
+#define WDT_CLK_NAME		"periph"

 struct bcm63xx_wdt_hw {
 	raw_spinlock_t lock;
 	void __iomem *regs;
+	struct clk *clk;
+	unsigned long clock_hz;
 	bool running;
 };

@@ -51,7 +54,7 @@ static int bcm63xx_wdt_start(struct watchdog_device *wdd)
 	unsigned long flags;

 	raw_spin_lock_irqsave(&hw->lock, flags);
-	bcm_writel(wdd->timeout * WDT_HZ, hw->regs + WDT_DEFVAL_REG);
+	bcm_writel(wdd->timeout * hw->clock_hz, hw->regs + WDT_DEFVAL_REG);
 	bcm_writel(WDT_START_1, hw->regs + WDT_CTL_REG);
 	bcm_writel(WDT_START_2, hw->regs + WDT_CTL_REG);
 	hw->running = true;
@@ -116,7 +119,7 @@ static void bcm63xx_wdt_isr(void *data)
 			die(PFX ": watchdog timer expired\n", get_irq_regs());
 		}

-		ms = timeleft / (WDT_HZ / 1000);
+		ms = timeleft / (hw->clock_hz / 1000);
 		dev_alert(wdd->dev,
 			"warning timer fired, reboot in %ums\n", ms);
 	}
@@ -160,14 +163,32 @@ static int bcm63xx_wdt_probe(struct platform_device *pdev)
 		return -ENXIO;
 	}

+	hw->clk = devm_clk_get(&pdev->dev, WDT_CLK_NAME);
+	if (IS_ERR(hw->clk)) {
+		if (PTR_ERR(hw->clk) != -EPROBE_DEFER)
+			dev_err(&pdev->dev, "unable to request clock\n");
+		return PTR_ERR(hw->clk);
+	}
+
+	hw->clock_hz = clk_get_rate(hw->clk);
+	if (!hw->clock_hz) {
+		dev_err(&pdev->dev, "unable to fetch clock rate\n");
+		return -EINVAL;
+	}
+
+	ret = clk_prepare_enable(hw->clk);
+	if (ret) {
+		dev_err(&pdev->dev, "unable to enable clock\n");
+		return ret;
+	}
+
 	raw_spin_lock_init(&hw->lock);
-	hw->running = false;

 	wdd->parent = &pdev->dev;
 	wdd->ops = &bcm63xx_wdt_ops;
 	wdd->info = &bcm63xx_wdt_info;
 	wdd->min_timeout = 1;
-	wdd->max_timeout = 0xffffffff / WDT_HZ;
+	wdd->max_timeout = 0xffffffff / hw->clock_hz;
 	wdd->timeout = min(30U, wdd->max_timeout);

 	watchdog_set_drvdata(wdd, hw);
@@ -179,7 +200,7 @@ static int bcm63xx_wdt_probe(struct platform_device *pdev)
 	ret = bcm63xx_timer_register(TIMER_WDT_ID, bcm63xx_wdt_isr, wdd);
 	if (ret < 0) {
 		dev_err(&pdev->dev, "failed to register wdt timer isr\n");
-		return ret;
+		goto disable_clk;
 	}

 	ret = watchdog_register_device(wdd);
@@ -197,15 +218,20 @@ static int bcm63xx_wdt_probe(struct platform_device *pdev)

 unregister_timer:
 	bcm63xx_timer_unregister(TIMER_WDT_ID);
+
+disable_clk:
+	clk_disable_unprepare(hw->clk);
 	return ret;
 }

 static int bcm63xx_wdt_remove(struct platform_device *pdev)
 {
 	struct watchdog_device *wdd = platform_get_drvdata(pdev);
+	struct bcm63xx_wdt_hw *hw = watchdog_get_drvdata(wdd);

 	bcm63xx_timer_unregister(TIMER_WDT_ID);
 	watchdog_unregister_device(wdd);
+	clk_disable_unprepare(hw->clk);
 	return 0;
 }

-- 
2.1.4

-- 
Simon Arlott

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

* [PATCH (v2) 7/10] watchdog: bcm63xx_wdt: Add get_timeleft function
@ 2015-11-24 22:15               ` Simon Arlott
  0 siblings, 0 replies; 95+ messages in thread
From: Simon Arlott @ 2015-11-24 22:15 UTC (permalink / raw)
  To: linux-watchdog
  Cc: Guenter Roeck, devicetree, Ralf Baechle, Thomas Gleixner,
	Jason Cooper, Marc Zyngier, Kevin Cernekee, Florian Fainelli,
	Wim Van Sebroeck, Maxime Bizon, Linux Kernel Mailing List,
	linux-mips, Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell,
	Kumar Gala, Jonas Gorski

Return the remaining time from the hardware control register.

Warn when the device is registered if the hardware watchdog is currently
running and report the remaining time left.

Signed-off-by: Simon Arlott <simon@fire.lp0.eu>
---
Changed "if (timeleft > 0)" to "if (hw->running)" when checking if a
warning should be printed, in case the time left is truncated down to
0 seconds.

 drivers/watchdog/bcm63xx_wdt.c | 37 +++++++++++++++++++++++++++++++++++++
 1 file changed, 37 insertions(+)

diff --git a/drivers/watchdog/bcm63xx_wdt.c b/drivers/watchdog/bcm63xx_wdt.c
index 3c7667a..9d099e0 100644
--- a/drivers/watchdog/bcm63xx_wdt.c
+++ b/drivers/watchdog/bcm63xx_wdt.c
@@ -14,6 +14,7 @@
 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt

 #include <linux/clk.h>
+#include <linux/delay.h>
 #include <linux/errno.h>
 #include <linux/io.h>
 #include <linux/kernel.h>
@@ -75,6 +76,19 @@ static int bcm63xx_wdt_stop(struct watchdog_device *wdd)
 	return 0;
 }

+static unsigned int bcm63xx_wdt_get_timeleft(struct watchdog_device *wdd)
+{
+	struct bcm63xx_wdt_hw *hw = watchdog_get_drvdata(wdd);
+	unsigned long flags;
+	u32 val;
+
+	raw_spin_lock_irqsave(&hw->lock, flags);
+	val = __raw_readl(hw->regs + WDT_CTL_REG);
+	val /= hw->clock_hz;
+	raw_spin_unlock_irqrestore(&hw->lock, flags);
+	return val;
+}
+
 static int bcm63xx_wdt_set_timeout(struct watchdog_device *wdd,
 	unsigned int timeout)
 {
@@ -130,6 +144,7 @@ static struct watchdog_ops bcm63xx_wdt_ops = {
 	.owner = THIS_MODULE,
 	.start = bcm63xx_wdt_start,
 	.stop = bcm63xx_wdt_stop,
+	.get_timeleft = bcm63xx_wdt_get_timeleft,
 	.set_timeout = bcm63xx_wdt_set_timeout,
 };

@@ -144,6 +159,8 @@ static int bcm63xx_wdt_probe(struct platform_device *pdev)
 	struct bcm63xx_wdt_hw *hw;
 	struct watchdog_device *wdd;
 	struct resource *r;
+	u32 timeleft1, timeleft2;
+	unsigned int timeleft;
 	int ret;

 	hw = devm_kzalloc(&pdev->dev, sizeof(*hw), GFP_KERNEL);
@@ -197,6 +214,23 @@ static int bcm63xx_wdt_probe(struct platform_device *pdev)
 	watchdog_init_timeout(wdd, 0, &pdev->dev);
 	watchdog_set_nowayout(wdd, nowayout);

+	/* Compare two reads of the time left value, 2 clock ticks apart */
+	rmb();
+	timeleft1 = __raw_readl(hw->regs + WDT_CTL_REG);
+	udelay(DIV_ROUND_UP(1000000, hw->clock_hz / 2));
+	/* Ensure the register is read twice */
+	rmb();
+	timeleft2 = __raw_readl(hw->regs + WDT_CTL_REG);
+
+	/* If the time left is changing, the watchdog is running */
+	if (timeleft1 != timeleft2) {
+		hw->running = true;
+		timeleft = bcm63xx_wdt_get_timeleft(wdd);
+	} else {
+		hw->running = false;
+		timeleft = 0;
+	}
+
 	ret = bcm63xx_timer_register(TIMER_WDT_ID, bcm63xx_wdt_isr, wdd);
 	if (ret < 0) {
 		dev_err(&pdev->dev, "failed to register wdt timer isr\n");
@@ -214,6 +248,8 @@ static int bcm63xx_wdt_probe(struct platform_device *pdev)
 		dev_name(wdd->dev), hw->regs,
 		wdd->timeout, wdd->max_timeout);

+	if (hw->running)
+		dev_alert(wdd->dev, "running, reboot in %us\n", timeleft);
 	return 0;

 unregister_timer:
@@ -255,6 +291,7 @@ module_platform_driver(bcm63xx_wdt_driver);

 MODULE_AUTHOR("Miguel Gaio <miguel.gaio@efixo.com>");
 MODULE_AUTHOR("Florian Fainelli <florian@openwrt.org>");
+MODULE_AUTHOR("Simon Arlott");
 MODULE_DESCRIPTION("Driver for the Broadcom BCM63xx SoC watchdog");
 MODULE_LICENSE("GPL");
 MODULE_ALIAS("platform:bcm63xx-wdt");
-- 
2.1.4

-- 
Simon Arlott

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

* [PATCH (v2) 7/10] watchdog: bcm63xx_wdt: Add get_timeleft function
@ 2015-11-24 22:15               ` Simon Arlott
  0 siblings, 0 replies; 95+ messages in thread
From: Simon Arlott @ 2015-11-24 22:15 UTC (permalink / raw)
  To: linux-watchdog-u79uwXL29TY76Z2rM5mHXA
  Cc: Guenter Roeck, devicetree-u79uwXL29TY76Z2rM5mHXA, Ralf Baechle,
	Thomas Gleixner, Jason Cooper, Marc Zyngier, Kevin Cernekee,
	Florian Fainelli, Wim Van Sebroeck, Maxime Bizon,
	Linux Kernel Mailing List, linux-mips-6z/3iImG2C8G8FEW9MqTrA,
	Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
	Jonas Gorski

Return the remaining time from the hardware control register.

Warn when the device is registered if the hardware watchdog is currently
running and report the remaining time left.

Signed-off-by: Simon Arlott <simon-A6De1vDTPLDsq35pWSNszA@public.gmane.org>
---
Changed "if (timeleft > 0)" to "if (hw->running)" when checking if a
warning should be printed, in case the time left is truncated down to
0 seconds.

 drivers/watchdog/bcm63xx_wdt.c | 37 +++++++++++++++++++++++++++++++++++++
 1 file changed, 37 insertions(+)

diff --git a/drivers/watchdog/bcm63xx_wdt.c b/drivers/watchdog/bcm63xx_wdt.c
index 3c7667a..9d099e0 100644
--- a/drivers/watchdog/bcm63xx_wdt.c
+++ b/drivers/watchdog/bcm63xx_wdt.c
@@ -14,6 +14,7 @@
 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt

 #include <linux/clk.h>
+#include <linux/delay.h>
 #include <linux/errno.h>
 #include <linux/io.h>
 #include <linux/kernel.h>
@@ -75,6 +76,19 @@ static int bcm63xx_wdt_stop(struct watchdog_device *wdd)
 	return 0;
 }

+static unsigned int bcm63xx_wdt_get_timeleft(struct watchdog_device *wdd)
+{
+	struct bcm63xx_wdt_hw *hw = watchdog_get_drvdata(wdd);
+	unsigned long flags;
+	u32 val;
+
+	raw_spin_lock_irqsave(&hw->lock, flags);
+	val = __raw_readl(hw->regs + WDT_CTL_REG);
+	val /= hw->clock_hz;
+	raw_spin_unlock_irqrestore(&hw->lock, flags);
+	return val;
+}
+
 static int bcm63xx_wdt_set_timeout(struct watchdog_device *wdd,
 	unsigned int timeout)
 {
@@ -130,6 +144,7 @@ static struct watchdog_ops bcm63xx_wdt_ops = {
 	.owner = THIS_MODULE,
 	.start = bcm63xx_wdt_start,
 	.stop = bcm63xx_wdt_stop,
+	.get_timeleft = bcm63xx_wdt_get_timeleft,
 	.set_timeout = bcm63xx_wdt_set_timeout,
 };

@@ -144,6 +159,8 @@ static int bcm63xx_wdt_probe(struct platform_device *pdev)
 	struct bcm63xx_wdt_hw *hw;
 	struct watchdog_device *wdd;
 	struct resource *r;
+	u32 timeleft1, timeleft2;
+	unsigned int timeleft;
 	int ret;

 	hw = devm_kzalloc(&pdev->dev, sizeof(*hw), GFP_KERNEL);
@@ -197,6 +214,23 @@ static int bcm63xx_wdt_probe(struct platform_device *pdev)
 	watchdog_init_timeout(wdd, 0, &pdev->dev);
 	watchdog_set_nowayout(wdd, nowayout);

+	/* Compare two reads of the time left value, 2 clock ticks apart */
+	rmb();
+	timeleft1 = __raw_readl(hw->regs + WDT_CTL_REG);
+	udelay(DIV_ROUND_UP(1000000, hw->clock_hz / 2));
+	/* Ensure the register is read twice */
+	rmb();
+	timeleft2 = __raw_readl(hw->regs + WDT_CTL_REG);
+
+	/* If the time left is changing, the watchdog is running */
+	if (timeleft1 != timeleft2) {
+		hw->running = true;
+		timeleft = bcm63xx_wdt_get_timeleft(wdd);
+	} else {
+		hw->running = false;
+		timeleft = 0;
+	}
+
 	ret = bcm63xx_timer_register(TIMER_WDT_ID, bcm63xx_wdt_isr, wdd);
 	if (ret < 0) {
 		dev_err(&pdev->dev, "failed to register wdt timer isr\n");
@@ -214,6 +248,8 @@ static int bcm63xx_wdt_probe(struct platform_device *pdev)
 		dev_name(wdd->dev), hw->regs,
 		wdd->timeout, wdd->max_timeout);

+	if (hw->running)
+		dev_alert(wdd->dev, "running, reboot in %us\n", timeleft);
 	return 0;

 unregister_timer:
@@ -255,6 +291,7 @@ module_platform_driver(bcm63xx_wdt_driver);

 MODULE_AUTHOR("Miguel Gaio <miguel.gaio-HH44TBFINEIAvxtiuMwx3w@public.gmane.org>");
 MODULE_AUTHOR("Florian Fainelli <florian-p3rKhJxN3npAfugRpC6u6w@public.gmane.org>");
+MODULE_AUTHOR("Simon Arlott");
 MODULE_DESCRIPTION("Driver for the Broadcom BCM63xx SoC watchdog");
 MODULE_LICENSE("GPL");
 MODULE_ALIAS("platform:bcm63xx-wdt");
-- 
2.1.4

-- 
Simon Arlott
--
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] 95+ messages in thread

* Re: [PATCH (v3) 2/10] MIPS: bmips: Add bcm6345-l2-timer interrupt controller
@ 2015-11-24 22:36           ` Florian Fainelli
  0 siblings, 0 replies; 95+ messages in thread
From: Florian Fainelli @ 2015-11-24 22:36 UTC (permalink / raw)
  To: Simon Arlott, MIPS Mailing List
  Cc: Jonas Gorski, devicetree, Ralf Baechle, Thomas Gleixner,
	Jason Cooper, Marc Zyngier, Kevin Cernekee, Wim Van Sebroeck,
	Miguel Gaio, Maxime Bizon, Linux Kernel Mailing List,
	linux-watchdog, Rob Herring, Pawel Moll, Mark Rutland,
	Ian Campbell, Kumar Gala

On 24/11/15 14:10, Simon Arlott wrote:
> Add the BCM6345/BCM6318 timer as an interrupt controller so that it can be
> used by the watchdog to warn that its timer will expire soon.
> 
> Support for clocksource/clockevents is not implemented as the timer
> interrupt is not per CPU (except on the BCM6318) and the MIPS clock is
> better. This could be added later if required without changing the device
> tree binding.
> 
> Signed-off-by: Simon Arlott <simon@fire.lp0.eu>
> ---
> Fixed the offset of the count registers, they were writing off by one which
> caused it to set the watchdog timeout to 0.
> 
>  drivers/irqchip/Kconfig                |   5 +
>  drivers/irqchip/Makefile               |   1 +
>  drivers/irqchip/irq-bcm6345-l2-timer.c | 324 +++++++++++++++++++++++++++++++++
>  3 files changed, 330 insertions(+)
>  create mode 100644 drivers/irqchip/irq-bcm6345-l2-timer.c
> 
> diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
> index d307bb3..21c3d9b 100644
> --- a/drivers/irqchip/Kconfig
> +++ b/drivers/irqchip/Kconfig
> @@ -70,6 +70,11 @@ config BCM6345_L1_IRQ
>  	select GENERIC_IRQ_CHIP
>  	select IRQ_DOMAIN
> 
> +config BCM6345_L2_TIMER_IRQ
> +	bool
> +	select GENERIC_IRQ_CHIP
> +	select IRQ_DOMAIN
> +
>  config BCM7038_L1_IRQ
>  	bool
>  	select GENERIC_IRQ_CHIP
> diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
> index ded59cf..2687dea 100644
> --- a/drivers/irqchip/Makefile
> +++ b/drivers/irqchip/Makefile
> @@ -44,6 +44,7 @@ obj-$(CONFIG_XTENSA_MX)			+= irq-xtensa-mx.o
>  obj-$(CONFIG_IRQ_CROSSBAR)		+= irq-crossbar.o
>  obj-$(CONFIG_SOC_VF610)			+= irq-vf610-mscm-ir.o
>  obj-$(CONFIG_BCM6345_L1_IRQ)		+= irq-bcm6345-l1.o
> +obj-$(CONFIG_BCM6345_L2_TIMER_IRQ)	+= irq-bcm6345-l2-timer.o
>  obj-$(CONFIG_BCM7038_L1_IRQ)		+= irq-bcm7038-l1.o
>  obj-$(CONFIG_BCM7120_L2_IRQ)		+= irq-bcm7120-l2.o
>  obj-$(CONFIG_BRCMSTB_L2_IRQ)		+= irq-brcmstb-l2.o
> diff --git a/drivers/irqchip/irq-bcm6345-l2-timer.c b/drivers/irqchip/irq-bcm6345-l2-timer.c
> new file mode 100644
> index 0000000..4e6f71b
> --- /dev/null
> +++ b/drivers/irqchip/irq-bcm6345-l2-timer.c
> @@ -0,0 +1,324 @@
> +/*
> + * Copyright 2015 Simon Arlott
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * Based on arch/mips/bcm63xx/timer.c:
> + * Copyright (C) 2008 Maxime Bizon <mbizon@freebox.fr>
> + *
> + * Registers for SoCs with 4 timers: BCM6345, BCM6328, BCM6362, BCM6816,
> + *                                   BCM68220,BCM63168, BCM63268
> + *   0x02: IRQ enable (u8)
> + *   0x03: IRQ status (u8)
> + *   0x04: Timer 0 control
> + *   0x08: Timer 1 control
> + *   0x0c: Timer 2 control
> + *   0x10: Timer 0 count
> + *   0x14: Timer 1 count
> + *   0x18: Timer 2 count
> + *   0x1c+: Watchdog registers
> + *
> + * Registers for SoCs with 5 timers: BCM6318
> + *   0x00: IRQ enable (u32)
> + *   0x04: IRQ status (u32)
> + *   0x08: Timer 0 control
> + *   0x0c: Timer 1 control
> + *   0x10: Timer 2 control
> + *   0x14: Timer 3 control
> + *   0x18: Timer 0 count
> + *   0x1c: Timer 1 count
> + *   0x20: Timer 2 count
> + *   0x24: Timer 3 count
> + *   0x28+: Watchdog registers
> + */
> +
> +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
> +
> +#include <linux/bitops.h>
> +#include <linux/interrupt.h>
> +#include <linux/irqreturn.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/of_address.h>
> +#include <linux/of_irq.h>
> +#include <linux/of_platform.h>
> +#include <linux/slab.h>
> +#include <linux/spinlock.h>
> +#include <linux/string.h>
> +#include <linux/irqchip.h>
> +#include <linux/irqchip/chained_irq.h>
> +
> +#define REG_6345_IRQ_ENABLE		0x02
> +#define REG_6345_IRQ_STATUS		0x03
> +#define REG_6345_CONTROL_BASE		0x04
> +#define REG_6345_COUNT_BASE		0x10
> +
> +#define REG_6318_IRQ_ENABLE		0x00
> +#define REG_6318_IRQ_STATUS		0x04
> +#define REG_6318_CONTROL_BASE		0x08
> +#define REG_6318_COUNT_BASE		0x18
> +
> +#define NR_TIMERS_6345			4
> +#define WDT_TIMER_ID_6345		(NR_TIMERS_6345 - 1)
> +
> +#define NR_TIMERS_6318			5
> +#define WDT_TIMER_ID_6318		(NR_TIMERS_6318 - 1)
> +
> +/* Per-timer count register */
> +#define COUNT_MASK			(0x3fffffff)
> +
> +/* Per-timer control register */
> +#define CONTROL_COUNTDOWN_MASK		(0x3fffffff)
> +#define CONTROL_RSTCNTCLR_MASK		(1 << 30)
> +#define CONTROL_ENABLE_MASK		(1 << 31)
> +
> +enum bcm6345_timer_type {
> +	TIMER_TYPE_6345,
> +	TIMER_TYPE_6318,
> +};
> +
> +struct bcm6345_timer {
> +	raw_spinlock_t lock;
> +	void __iomem *base;
> +	unsigned int irq;
> +	struct irq_domain *domain;
> +
> +	enum bcm6345_timer_type type;
> +	unsigned int nr_timers;
> +	/* The watchdog timer has separate control/remaining registers
> +	 * and cannot be masked.
> +	 */
> +	int wdt_timer_id;
> +};
> +
> +static inline u32 bcm6345_timer_read_int_status(struct bcm6345_timer *timer)
> +{
> +	if (timer->type == TIMER_TYPE_6318)
> +		return __raw_readl(timer->base + REG_6318_IRQ_STATUS);
> +	else
> +		return __raw_readb(timer->base + REG_6345_IRQ_STATUS);

This is really error prone and does not scale to adding a third type of
timer if we ever had to. How about using the same logic and construction
as the following drivers: drivers/spi/spi-bcm63xx.c,
drivers/bus/brcmstb_gisb.c?
-- 
Florian

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

* Re: [PATCH (v3) 2/10] MIPS: bmips: Add bcm6345-l2-timer interrupt controller
@ 2015-11-24 22:36           ` Florian Fainelli
  0 siblings, 0 replies; 95+ messages in thread
From: Florian Fainelli @ 2015-11-24 22:36 UTC (permalink / raw)
  To: Simon Arlott, MIPS Mailing List
  Cc: Jonas Gorski, devicetree-u79uwXL29TY76Z2rM5mHXA, Ralf Baechle,
	Thomas Gleixner, Jason Cooper, Marc Zyngier, Kevin Cernekee,
	Wim Van Sebroeck, Miguel Gaio, Maxime Bizon,
	Linux Kernel Mailing List, linux-watchdog-u79uwXL29TY76Z2rM5mHXA,
	Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala

On 24/11/15 14:10, Simon Arlott wrote:
> Add the BCM6345/BCM6318 timer as an interrupt controller so that it can be
> used by the watchdog to warn that its timer will expire soon.
> 
> Support for clocksource/clockevents is not implemented as the timer
> interrupt is not per CPU (except on the BCM6318) and the MIPS clock is
> better. This could be added later if required without changing the device
> tree binding.
> 
> Signed-off-by: Simon Arlott <simon-A6De1vDTPLDsq35pWSNszA@public.gmane.org>
> ---
> Fixed the offset of the count registers, they were writing off by one which
> caused it to set the watchdog timeout to 0.
> 
>  drivers/irqchip/Kconfig                |   5 +
>  drivers/irqchip/Makefile               |   1 +
>  drivers/irqchip/irq-bcm6345-l2-timer.c | 324 +++++++++++++++++++++++++++++++++
>  3 files changed, 330 insertions(+)
>  create mode 100644 drivers/irqchip/irq-bcm6345-l2-timer.c
> 
> diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
> index d307bb3..21c3d9b 100644
> --- a/drivers/irqchip/Kconfig
> +++ b/drivers/irqchip/Kconfig
> @@ -70,6 +70,11 @@ config BCM6345_L1_IRQ
>  	select GENERIC_IRQ_CHIP
>  	select IRQ_DOMAIN
> 
> +config BCM6345_L2_TIMER_IRQ
> +	bool
> +	select GENERIC_IRQ_CHIP
> +	select IRQ_DOMAIN
> +
>  config BCM7038_L1_IRQ
>  	bool
>  	select GENERIC_IRQ_CHIP
> diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
> index ded59cf..2687dea 100644
> --- a/drivers/irqchip/Makefile
> +++ b/drivers/irqchip/Makefile
> @@ -44,6 +44,7 @@ obj-$(CONFIG_XTENSA_MX)			+= irq-xtensa-mx.o
>  obj-$(CONFIG_IRQ_CROSSBAR)		+= irq-crossbar.o
>  obj-$(CONFIG_SOC_VF610)			+= irq-vf610-mscm-ir.o
>  obj-$(CONFIG_BCM6345_L1_IRQ)		+= irq-bcm6345-l1.o
> +obj-$(CONFIG_BCM6345_L2_TIMER_IRQ)	+= irq-bcm6345-l2-timer.o
>  obj-$(CONFIG_BCM7038_L1_IRQ)		+= irq-bcm7038-l1.o
>  obj-$(CONFIG_BCM7120_L2_IRQ)		+= irq-bcm7120-l2.o
>  obj-$(CONFIG_BRCMSTB_L2_IRQ)		+= irq-brcmstb-l2.o
> diff --git a/drivers/irqchip/irq-bcm6345-l2-timer.c b/drivers/irqchip/irq-bcm6345-l2-timer.c
> new file mode 100644
> index 0000000..4e6f71b
> --- /dev/null
> +++ b/drivers/irqchip/irq-bcm6345-l2-timer.c
> @@ -0,0 +1,324 @@
> +/*
> + * Copyright 2015 Simon Arlott
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * Based on arch/mips/bcm63xx/timer.c:
> + * Copyright (C) 2008 Maxime Bizon <mbizon-MmRyKUhfbQ9GWvitb5QawA@public.gmane.org>
> + *
> + * Registers for SoCs with 4 timers: BCM6345, BCM6328, BCM6362, BCM6816,
> + *                                   BCM68220,BCM63168, BCM63268
> + *   0x02: IRQ enable (u8)
> + *   0x03: IRQ status (u8)
> + *   0x04: Timer 0 control
> + *   0x08: Timer 1 control
> + *   0x0c: Timer 2 control
> + *   0x10: Timer 0 count
> + *   0x14: Timer 1 count
> + *   0x18: Timer 2 count
> + *   0x1c+: Watchdog registers
> + *
> + * Registers for SoCs with 5 timers: BCM6318
> + *   0x00: IRQ enable (u32)
> + *   0x04: IRQ status (u32)
> + *   0x08: Timer 0 control
> + *   0x0c: Timer 1 control
> + *   0x10: Timer 2 control
> + *   0x14: Timer 3 control
> + *   0x18: Timer 0 count
> + *   0x1c: Timer 1 count
> + *   0x20: Timer 2 count
> + *   0x24: Timer 3 count
> + *   0x28+: Watchdog registers
> + */
> +
> +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
> +
> +#include <linux/bitops.h>
> +#include <linux/interrupt.h>
> +#include <linux/irqreturn.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/of_address.h>
> +#include <linux/of_irq.h>
> +#include <linux/of_platform.h>
> +#include <linux/slab.h>
> +#include <linux/spinlock.h>
> +#include <linux/string.h>
> +#include <linux/irqchip.h>
> +#include <linux/irqchip/chained_irq.h>
> +
> +#define REG_6345_IRQ_ENABLE		0x02
> +#define REG_6345_IRQ_STATUS		0x03
> +#define REG_6345_CONTROL_BASE		0x04
> +#define REG_6345_COUNT_BASE		0x10
> +
> +#define REG_6318_IRQ_ENABLE		0x00
> +#define REG_6318_IRQ_STATUS		0x04
> +#define REG_6318_CONTROL_BASE		0x08
> +#define REG_6318_COUNT_BASE		0x18
> +
> +#define NR_TIMERS_6345			4
> +#define WDT_TIMER_ID_6345		(NR_TIMERS_6345 - 1)
> +
> +#define NR_TIMERS_6318			5
> +#define WDT_TIMER_ID_6318		(NR_TIMERS_6318 - 1)
> +
> +/* Per-timer count register */
> +#define COUNT_MASK			(0x3fffffff)
> +
> +/* Per-timer control register */
> +#define CONTROL_COUNTDOWN_MASK		(0x3fffffff)
> +#define CONTROL_RSTCNTCLR_MASK		(1 << 30)
> +#define CONTROL_ENABLE_MASK		(1 << 31)
> +
> +enum bcm6345_timer_type {
> +	TIMER_TYPE_6345,
> +	TIMER_TYPE_6318,
> +};
> +
> +struct bcm6345_timer {
> +	raw_spinlock_t lock;
> +	void __iomem *base;
> +	unsigned int irq;
> +	struct irq_domain *domain;
> +
> +	enum bcm6345_timer_type type;
> +	unsigned int nr_timers;
> +	/* The watchdog timer has separate control/remaining registers
> +	 * and cannot be masked.
> +	 */
> +	int wdt_timer_id;
> +};
> +
> +static inline u32 bcm6345_timer_read_int_status(struct bcm6345_timer *timer)
> +{
> +	if (timer->type == TIMER_TYPE_6318)
> +		return __raw_readl(timer->base + REG_6318_IRQ_STATUS);
> +	else
> +		return __raw_readb(timer->base + REG_6345_IRQ_STATUS);

This is really error prone and does not scale to adding a third type of
timer if we ever had to. How about using the same logic and construction
as the following drivers: drivers/spi/spi-bcm63xx.c,
drivers/bus/brcmstb_gisb.c?
-- 
Florian
--
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] 95+ messages in thread

* Re: [PATCH (v2) 6/10] watchdog: bcm63xx_wdt: Obtain watchdog clock HZ from "periph" clk
@ 2015-11-24 22:42                 ` Florian Fainelli
  0 siblings, 0 replies; 95+ messages in thread
From: Florian Fainelli @ 2015-11-24 22:42 UTC (permalink / raw)
  To: Simon Arlott, linux-watchdog
  Cc: Guenter Roeck, devicetree, Ralf Baechle, Thomas Gleixner,
	Jason Cooper, Marc Zyngier, Kevin Cernekee, Wim Van Sebroeck,
	Maxime Bizon, Linux Kernel Mailing List, linux-mips, Rob Herring,
	Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, Jonas Gorski

On 24/11/15 14:12, Simon Arlott wrote:
> Instead of using a fixed clock HZ in the driver, obtain it from the
> "periph" clk that the watchdog timer uses.
> 
> Signed-off-by: Simon Arlott <simon@fire.lp0.eu>

Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
-- 
Florian

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

* Re: [PATCH (v2) 6/10] watchdog: bcm63xx_wdt: Obtain watchdog clock HZ from "periph" clk
@ 2015-11-24 22:42                 ` Florian Fainelli
  0 siblings, 0 replies; 95+ messages in thread
From: Florian Fainelli @ 2015-11-24 22:42 UTC (permalink / raw)
  To: Simon Arlott, linux-watchdog-u79uwXL29TY76Z2rM5mHXA
  Cc: Guenter Roeck, devicetree-u79uwXL29TY76Z2rM5mHXA, Ralf Baechle,
	Thomas Gleixner, Jason Cooper, Marc Zyngier, Kevin Cernekee,
	Wim Van Sebroeck, Maxime Bizon, Linux Kernel Mailing List,
	linux-mips-6z/3iImG2C8G8FEW9MqTrA, Rob Herring, Pawel Moll,
	Mark Rutland, Ian Campbell, Kumar Gala, Jonas Gorski

On 24/11/15 14:12, Simon Arlott wrote:
> Instead of using a fixed clock HZ in the driver, obtain it from the
> "periph" clk that the watchdog timer uses.
> 
> Signed-off-by: Simon Arlott <simon-A6De1vDTPLDsq35pWSNszA@public.gmane.org>

Reviewed-by: Florian Fainelli <f.fainelli-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
-- 
Florian
--
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] 95+ messages in thread

* Re: [PATCH (v2) 7/10] watchdog: bcm63xx_wdt: Add get_timeleft function
@ 2015-11-24 22:43                 ` Florian Fainelli
  0 siblings, 0 replies; 95+ messages in thread
From: Florian Fainelli @ 2015-11-24 22:43 UTC (permalink / raw)
  To: Simon Arlott, linux-watchdog
  Cc: Guenter Roeck, devicetree, Ralf Baechle, Thomas Gleixner,
	Jason Cooper, Marc Zyngier, Kevin Cernekee, Wim Van Sebroeck,
	Maxime Bizon, Linux Kernel Mailing List, linux-mips, Rob Herring,
	Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, Jonas Gorski

On 24/11/15 14:15, Simon Arlott wrote:
> Return the remaining time from the hardware control register.
> 
> Warn when the device is registered if the hardware watchdog is currently
> running and report the remaining time left.
> 
> Signed-off-by: Simon Arlott <simon@fire.lp0.eu>

Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>

At some point, we should probably consider merging bcm63xx_wdt with
bcm7038_wdt which are nearly 100% identical pieces of hardware (coming
from the same design group originally).
-- 
Florian

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

* Re: [PATCH (v2) 7/10] watchdog: bcm63xx_wdt: Add get_timeleft function
@ 2015-11-24 22:43                 ` Florian Fainelli
  0 siblings, 0 replies; 95+ messages in thread
From: Florian Fainelli @ 2015-11-24 22:43 UTC (permalink / raw)
  To: Simon Arlott, linux-watchdog-u79uwXL29TY76Z2rM5mHXA
  Cc: Guenter Roeck, devicetree-u79uwXL29TY76Z2rM5mHXA, Ralf Baechle,
	Thomas Gleixner, Jason Cooper, Marc Zyngier, Kevin Cernekee,
	Wim Van Sebroeck, Maxime Bizon, Linux Kernel Mailing List,
	linux-mips-6z/3iImG2C8G8FEW9MqTrA, Rob Herring, Pawel Moll,
	Mark Rutland, Ian Campbell, Kumar Gala, Jonas Gorski

On 24/11/15 14:15, Simon Arlott wrote:
> Return the remaining time from the hardware control register.
> 
> Warn when the device is registered if the hardware watchdog is currently
> running and report the remaining time left.
> 
> Signed-off-by: Simon Arlott <simon-A6De1vDTPLDsq35pWSNszA@public.gmane.org>

Reviewed-by: Florian Fainelli <f.fainelli-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>

At some point, we should probably consider merging bcm63xx_wdt with
bcm7038_wdt which are nearly 100% identical pieces of hardware (coming
from the same design group originally).
-- 
Florian
--
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] 95+ messages in thread

* Re: [PATCH 5/10] watchdog: bcm63xx_wdt: Use WATCHDOG_CORE
@ 2015-11-25  2:44               ` Guenter Roeck
  0 siblings, 0 replies; 95+ messages in thread
From: Guenter Roeck @ 2015-11-25  2:44 UTC (permalink / raw)
  To: Simon Arlott, devicetree, Ralf Baechle, Thomas Gleixner,
	Jason Cooper, Marc Zyngier, Kevin Cernekee, Florian Fainelli,
	Wim Van Sebroeck, Maxime Bizon, Linux Kernel Mailing List,
	linux-mips, linux-watchdog
  Cc: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
	Jonas Gorski

Hi Simon,

On 11/22/2015 06:06 AM, Simon Arlott wrote:
> Convert bcm63xx_wdt to use WATCHDOG_CORE.
>
> The default and maximum time constants that are only used once have been
> moved to the initialisation of the struct watchdog_device.
>
Comments inline.

Thanks,
Guenter

> Signed-off-by: Simon Arlott <simon@fire.lp0.eu>
> ---
>   drivers/watchdog/Kconfig       |   1 +
>   drivers/watchdog/bcm63xx_wdt.c | 249 ++++++++++++-----------------------------
>   2 files changed, 74 insertions(+), 176 deletions(-)
>
> diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
> index 7a8a6c6..6815b74 100644
> --- a/drivers/watchdog/Kconfig
> +++ b/drivers/watchdog/Kconfig
> @@ -1273,6 +1273,7 @@ config OCTEON_WDT
>   config BCM63XX_WDT
>   	tristate "Broadcom BCM63xx hardware watchdog"
>   	depends on BCM63XX
> +	select WATCHDOG_CORE
>   	help
>   	  Watchdog driver for the built in watchdog hardware in Broadcom
>   	  BCM63xx SoC.
> diff --git a/drivers/watchdog/bcm63xx_wdt.c b/drivers/watchdog/bcm63xx_wdt.c
> index f88fc97..1d2a501 100644
> --- a/drivers/watchdog/bcm63xx_wdt.c
> +++ b/drivers/watchdog/bcm63xx_wdt.c
> @@ -13,20 +13,15 @@
>
>   #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
>
> -#include <linux/bitops.h>
>   #include <linux/errno.h>
> -#include <linux/fs.h>
>   #include <linux/io.h>
>   #include <linux/kernel.h>
> -#include <linux/miscdevice.h>
>   #include <linux/module.h>
>   #include <linux/moduleparam.h>
>   #include <linux/spinlock.h>
>   #include <linux/types.h>
> -#include <linux/uaccess.h>
>   #include <linux/watchdog.h>
>   #include <linux/interrupt.h>
> -#include <linux/ptrace.h>
>   #include <linux/resource.h>
>   #include <linux/platform_device.h>
>
> @@ -38,53 +33,57 @@
>   #define PFX KBUILD_MODNAME
>
>   #define WDT_HZ			50000000		/* Fclk */
> -#define WDT_DEFAULT_TIME	30			/* seconds */
> -#define WDT_MAX_TIME		(0xffffffff / WDT_HZ)	/* seconds */
>
>   struct bcm63xx_wdt_hw {
>   	raw_spinlock_t lock;
>   	void __iomem *regs;
> -	unsigned long inuse;
>   	bool running;

The "running" flag should no longer be needed. watchdog_active()
should provide that information.

>   };
> -static struct bcm63xx_wdt_hw bcm63xx_wdt_device;
>
> -static int expect_close;
> -
> -static int wdt_time = WDT_DEFAULT_TIME;
>   static bool nowayout = WATCHDOG_NOWAYOUT;
>   module_param(nowayout, bool, 0);
>   MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
>   	__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
>
> -/* HW functions */
> -static void bcm63xx_wdt_hw_start(void)
> +static int bcm63xx_wdt_start(struct watchdog_device *wdd)
>   {
> +	struct bcm63xx_wdt_hw *hw = watchdog_get_drvdata(wdd);
>   	unsigned long flags;
>
> -	raw_spin_lock_irqsave(&bcm63xx_wdt_device.lock, flags);
> -	bcm_writel(wdt_time * WDT_HZ, bcm63xx_wdt_device.regs + WDT_DEFVAL_REG);
> -	bcm_writel(WDT_START_1, bcm63xx_wdt_device.regs + WDT_CTL_REG);
> -	bcm_writel(WDT_START_2, bcm63xx_wdt_device.regs + WDT_CTL_REG);
> -	bcm63xx_wdt_device.running = true;
> -	raw_spin_unlock_irqrestore(&bcm63xx_wdt_device.lock, flags);
> +	raw_spin_lock_irqsave(&hw->lock, flags);
> +	bcm_writel(wdd->timeout * WDT_HZ, hw->regs + WDT_DEFVAL_REG);
> +	bcm_writel(WDT_START_1, hw->regs + WDT_CTL_REG);
> +	bcm_writel(WDT_START_2, hw->regs + WDT_CTL_REG);
> +	hw->running = true;
> +	raw_spin_unlock_irqrestore(&hw->lock, flags);
> +	return 0;
>   }
>
> -static void bcm63xx_wdt_hw_stop(void)
> +static int bcm63xx_wdt_stop(struct watchdog_device *wdd)
>   {
> +	struct bcm63xx_wdt_hw *hw = watchdog_get_drvdata(wdd);
>   	unsigned long flags;
>
> -	raw_spin_lock_irqsave(&bcm63xx_wdt_device.lock, flags);
> -	bcm_writel(WDT_STOP_1, bcm63xx_wdt_device.regs + WDT_CTL_REG);
> -	bcm_writel(WDT_STOP_2, bcm63xx_wdt_device.regs + WDT_CTL_REG);
> -	bcm63xx_wdt_device.running = false;
> -	raw_spin_unlock_irqrestore(&bcm63xx_wdt_device.lock, flags);
> +	raw_spin_lock_irqsave(&hw->lock, flags);
> +	bcm_writel(WDT_STOP_1, hw->regs + WDT_CTL_REG);
> +	bcm_writel(WDT_STOP_2, hw->regs + WDT_CTL_REG);
> +	hw->running = false;
> +	raw_spin_unlock_irqrestore(&hw->lock, flags);
> +	return 0;
> +}
> +
> +static int bcm63xx_wdt_set_timeout(struct watchdog_device *wdd,
> +	unsigned int timeout)
> +{
> +	wdd->timeout = timeout;
> +	return bcm63xx_wdt_start(wdd);

If I see correctly, there is no ping function. In that case, the watchdog core
will call the start function after updating the timeout, so there is no need
to do it here.

>   }
>
>   /* The watchdog interrupt occurs when half the timeout is remaining */
>   static void bcm63xx_wdt_isr(void *data)
>   {
> -	struct bcm63xx_wdt_hw *hw = &bcm63xx_wdt_device;
> +	struct watchdog_device *wdd = data;
> +	struct bcm63xx_wdt_hw *hw = watchdog_get_drvdata(wdd);
>   	unsigned long flags;
>
>   	raw_spin_lock_irqsave(&hw->lock, flags);
> @@ -118,147 +117,36 @@ static void bcm63xx_wdt_isr(void *data)
>   		}
>
>   		ms = timeleft / (WDT_HZ / 1000);
> -		pr_alert("warning timer fired, reboot in %ums\n", ms);
> +		dev_alert(wdd->dev,
> +			"warning timer fired, reboot in %ums\n", ms);
>   	}
>   	raw_spin_unlock_irqrestore(&hw->lock, flags);
>   }
>
> -static int bcm63xx_wdt_settimeout(int new_time)
> -{
> -	if ((new_time <= 0) || (new_time > WDT_MAX_TIME))
> -		return -EINVAL;
> -
> -	wdt_time = new_time;
> -
> -	return 0;
> -}
> -
> -static int bcm63xx_wdt_open(struct inode *inode, struct file *file)
> -{
> -	if (test_and_set_bit(0, &bcm63xx_wdt_device.inuse))
> -		return -EBUSY;
> -
> -	bcm63xx_wdt_hw_start();
> -	return nonseekable_open(inode, file);
> -}
> -
> -static int bcm63xx_wdt_release(struct inode *inode, struct file *file)
> -{
> -	if (expect_close == 42)
> -		bcm63xx_wdt_hw_stop();
> -	else {
> -		pr_crit("Unexpected close, not stopping watchdog!\n");
> -		bcm63xx_wdt_hw_start();
> -	}
> -	clear_bit(0, &bcm63xx_wdt_device.inuse);
> -	expect_close = 0;
> -	return 0;
> -}
> -
> -static ssize_t bcm63xx_wdt_write(struct file *file, const char *data,
> -				size_t len, loff_t *ppos)
> -{
> -	if (len) {
> -		if (!nowayout) {
> -			size_t i;
> -
> -			/* In case it was set long ago */
> -			expect_close = 0;
> -
> -			for (i = 0; i != len; i++) {
> -				char c;
> -				if (get_user(c, data + i))
> -					return -EFAULT;
> -				if (c == 'V')
> -					expect_close = 42;
> -			}
> -		}
> -		bcm63xx_wdt_hw_start();
> -	}
> -	return len;
> -}
> -
> -static struct watchdog_info bcm63xx_wdt_info = {
> -	.identity       = PFX,
> -	.options        = WDIOF_SETTIMEOUT |
> -				WDIOF_KEEPALIVEPING |
> -				WDIOF_MAGICCLOSE,
> -};
> -
> -
> -static long bcm63xx_wdt_ioctl(struct file *file, unsigned int cmd,
> -				unsigned long arg)
> -{
> -	void __user *argp = (void __user *)arg;
> -	int __user *p = argp;
> -	int new_value, retval = -EINVAL;
> -
> -	switch (cmd) {
> -	case WDIOC_GETSUPPORT:
> -		return copy_to_user(argp, &bcm63xx_wdt_info,
> -			sizeof(bcm63xx_wdt_info)) ? -EFAULT : 0;
> -
> -	case WDIOC_GETSTATUS:
> -	case WDIOC_GETBOOTSTATUS:
> -		return put_user(0, p);
> -
> -	case WDIOC_SETOPTIONS:
> -		if (get_user(new_value, p))
> -			return -EFAULT;
> -
> -		if (new_value & WDIOS_DISABLECARD) {
> -			bcm63xx_wdt_hw_stop();
> -			retval = 0;
> -		}
> -		if (new_value & WDIOS_ENABLECARD) {
> -			bcm63xx_wdt_hw_start();
> -			retval = 0;
> -		}
> -
> -		return retval;
> -
> -	case WDIOC_KEEPALIVE:
> -		bcm63xx_wdt_hw_start();
> -		return 0;
> -
> -	case WDIOC_SETTIMEOUT:
> -		if (get_user(new_value, p))
> -			return -EFAULT;
> -
> -		if (bcm63xx_wdt_settimeout(new_value))
> -			return -EINVAL;
> -
> -		bcm63xx_wdt_hw_start();
> -
> -	case WDIOC_GETTIMEOUT:
> -		return put_user(wdt_time, p);
> -
> -	default:
> -		return -ENOTTY;
> -
> -	}
> -}
> -
> -static const struct file_operations bcm63xx_wdt_fops = {
> -	.owner		= THIS_MODULE,
> -	.llseek		= no_llseek,
> -	.write		= bcm63xx_wdt_write,
> -	.unlocked_ioctl	= bcm63xx_wdt_ioctl,
> -	.open		= bcm63xx_wdt_open,
> -	.release	= bcm63xx_wdt_release,
> +static struct watchdog_ops bcm63xx_wdt_ops = {
> +	.owner = THIS_MODULE,
> +	.start = bcm63xx_wdt_start,
> +	.stop = bcm63xx_wdt_stop,
> +	.set_timeout = bcm63xx_wdt_set_timeout,
>   };
>
> -static struct miscdevice bcm63xx_wdt_miscdev = {
> -	.minor	= WATCHDOG_MINOR,
> -	.name	= "watchdog",
> -	.fops	= &bcm63xx_wdt_fops,
> +static const struct watchdog_info bcm63xx_wdt_info = {
> +	.options = WDIOC_GETTIMELEFT | WDIOF_SETTIMEOUT |

Where is the gettimeleft function ? I think you are adding it with a later patch,
but then you should set the flag there, not here.

> +			WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
> +	.identity = "BCM63xx Watchdog",
>   };
>
> -
>   static int bcm63xx_wdt_probe(struct platform_device *pdev)
>   {
> -	int ret;
> +	struct bcm63xx_wdt_hw *hw;
> +	struct watchdog_device *wdd;
>   	struct resource *r;
> +	int ret;
> +
> +	hw = devm_kzalloc(&pdev->dev, sizeof(*hw), GFP_KERNEL);
> +	wdd = devm_kzalloc(&pdev->dev, sizeof(*wdd), GFP_KERNEL);

It would be better to allocate wdd as part of struct bcm63xx_wdt_hw.
Then you only need a single allocation. You can still use
	wdd = &hw->wdd;
to simplify the rest of this function.

> +	if (!hw || !wdd)
> +		return -ENOMEM;
>
>   	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>   	if (!r) {
> @@ -266,36 +154,44 @@ static int bcm63xx_wdt_probe(struct platform_device *pdev)
>   		return -ENODEV;
>   	}
>
> -	bcm63xx_wdt_device.regs = devm_ioremap_nocache(&pdev->dev, r->start,
> -							resource_size(r));
> -	if (!bcm63xx_wdt_device.regs) {
> +	hw->regs = devm_ioremap_nocache(&pdev->dev, r->start, resource_size(r));
> +	if (!hw->regs) {
>   		dev_err(&pdev->dev, "failed to remap I/O resources\n");
>   		return -ENXIO;
>   	}
>
> -	raw_spin_lock_init(&bcm63xx_wdt_device.lock);
> +	raw_spin_lock_init(&hw->lock);
> +	hw->running = false;
> +
> +	wdd->parent = &pdev->dev;
> +	wdd->ops = &bcm63xx_wdt_ops;
> +	wdd->info = &bcm63xx_wdt_info;
> +	wdd->min_timeout = 1;
> +	wdd->max_timeout = 0xffffffff / WDT_HZ;
> +	wdd->timeout = min(30U, wdd->max_timeout);
> +
> +	watchdog_set_drvdata(wdd, hw);
> +	platform_set_drvdata(pdev, wdd);
> +
> +	watchdog_init_timeout(wdd, 0, &pdev->dev);
> +	watchdog_set_nowayout(wdd, nowayout);
>
> -	ret = bcm63xx_timer_register(TIMER_WDT_ID, bcm63xx_wdt_isr, NULL);
> +	ret = bcm63xx_timer_register(TIMER_WDT_ID, bcm63xx_wdt_isr, wdd);
>   	if (ret < 0) {
>   		dev_err(&pdev->dev, "failed to register wdt timer isr\n");
>   		return ret;
>   	}
>
> -	if (bcm63xx_wdt_settimeout(wdt_time)) {
> -		bcm63xx_wdt_settimeout(WDT_DEFAULT_TIME);
> -		dev_info(&pdev->dev,
> -			": wdt_time value must be 1 <= wdt_time <= %d, using %d\n",
> -			WDT_MAX_TIME, wdt_time);
> -	}
> -
> -	ret = misc_register(&bcm63xx_wdt_miscdev);
> +	ret = watchdog_register_device(wdd);
>   	if (ret < 0) {
>   		dev_err(&pdev->dev, "failed to register watchdog device\n");
>   		goto unregister_timer;
>   	}
>
> -	dev_info(&pdev->dev, " started, timer margin: %d sec\n",
> -						WDT_DEFAULT_TIME);
> +	dev_info(&pdev->dev,
> +		"%s at MMIO 0x%p (timeout = %us, max_timeout = %us)",
> +		dev_name(wdd->dev), hw->regs,
> +		wdd->timeout, wdd->max_timeout);
>
>   	return 0;
>
> @@ -306,17 +202,18 @@ unregister_timer:
>
>   static int bcm63xx_wdt_remove(struct platform_device *pdev)
>   {
> -	if (!nowayout)
> -		bcm63xx_wdt_hw_stop();
> +	struct watchdog_device *wdd = platform_get_drvdata(pdev);
>
> -	misc_deregister(&bcm63xx_wdt_miscdev);
>   	bcm63xx_timer_unregister(TIMER_WDT_ID);
> +	watchdog_unregister_device(wdd);

Shouldn't that come first, before unregistering the timer ?

>   	return 0;
>   }
>
>   static void bcm63xx_wdt_shutdown(struct platform_device *pdev)
>   {
> -	bcm63xx_wdt_hw_stop();
> +	struct watchdog_device *wdd = platform_get_drvdata(pdev);
> +
> +	bcm63xx_wdt_stop(wdd);
>   }
>
>   static struct platform_driver bcm63xx_wdt_driver = {
>


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

* Re: [PATCH 5/10] watchdog: bcm63xx_wdt: Use WATCHDOG_CORE
@ 2015-11-25  2:44               ` Guenter Roeck
  0 siblings, 0 replies; 95+ messages in thread
From: Guenter Roeck @ 2015-11-25  2:44 UTC (permalink / raw)
  To: Simon Arlott, devicetree-u79uwXL29TY76Z2rM5mHXA, Ralf Baechle,
	Thomas Gleixner, Jason Cooper, Marc Zyngier, Kevin Cernekee,
	Florian Fainelli, Wim Van Sebroeck, Maxime Bizon,
	Linux Kernel Mailing List, linux-mips-6z/3iImG2C8G8FEW9MqTrA,
	linux-watchdog-u79uwXL29TY76Z2rM5mHXA
  Cc: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
	Jonas Gorski

Hi Simon,

On 11/22/2015 06:06 AM, Simon Arlott wrote:
> Convert bcm63xx_wdt to use WATCHDOG_CORE.
>
> The default and maximum time constants that are only used once have been
> moved to the initialisation of the struct watchdog_device.
>
Comments inline.

Thanks,
Guenter

> Signed-off-by: Simon Arlott <simon-A6De1vDTPLDsq35pWSNszA@public.gmane.org>
> ---
>   drivers/watchdog/Kconfig       |   1 +
>   drivers/watchdog/bcm63xx_wdt.c | 249 ++++++++++++-----------------------------
>   2 files changed, 74 insertions(+), 176 deletions(-)
>
> diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
> index 7a8a6c6..6815b74 100644
> --- a/drivers/watchdog/Kconfig
> +++ b/drivers/watchdog/Kconfig
> @@ -1273,6 +1273,7 @@ config OCTEON_WDT
>   config BCM63XX_WDT
>   	tristate "Broadcom BCM63xx hardware watchdog"
>   	depends on BCM63XX
> +	select WATCHDOG_CORE
>   	help
>   	  Watchdog driver for the built in watchdog hardware in Broadcom
>   	  BCM63xx SoC.
> diff --git a/drivers/watchdog/bcm63xx_wdt.c b/drivers/watchdog/bcm63xx_wdt.c
> index f88fc97..1d2a501 100644
> --- a/drivers/watchdog/bcm63xx_wdt.c
> +++ b/drivers/watchdog/bcm63xx_wdt.c
> @@ -13,20 +13,15 @@
>
>   #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
>
> -#include <linux/bitops.h>
>   #include <linux/errno.h>
> -#include <linux/fs.h>
>   #include <linux/io.h>
>   #include <linux/kernel.h>
> -#include <linux/miscdevice.h>
>   #include <linux/module.h>
>   #include <linux/moduleparam.h>
>   #include <linux/spinlock.h>
>   #include <linux/types.h>
> -#include <linux/uaccess.h>
>   #include <linux/watchdog.h>
>   #include <linux/interrupt.h>
> -#include <linux/ptrace.h>
>   #include <linux/resource.h>
>   #include <linux/platform_device.h>
>
> @@ -38,53 +33,57 @@
>   #define PFX KBUILD_MODNAME
>
>   #define WDT_HZ			50000000		/* Fclk */
> -#define WDT_DEFAULT_TIME	30			/* seconds */
> -#define WDT_MAX_TIME		(0xffffffff / WDT_HZ)	/* seconds */
>
>   struct bcm63xx_wdt_hw {
>   	raw_spinlock_t lock;
>   	void __iomem *regs;
> -	unsigned long inuse;
>   	bool running;

The "running" flag should no longer be needed. watchdog_active()
should provide that information.

>   };
> -static struct bcm63xx_wdt_hw bcm63xx_wdt_device;
>
> -static int expect_close;
> -
> -static int wdt_time = WDT_DEFAULT_TIME;
>   static bool nowayout = WATCHDOG_NOWAYOUT;
>   module_param(nowayout, bool, 0);
>   MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
>   	__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
>
> -/* HW functions */
> -static void bcm63xx_wdt_hw_start(void)
> +static int bcm63xx_wdt_start(struct watchdog_device *wdd)
>   {
> +	struct bcm63xx_wdt_hw *hw = watchdog_get_drvdata(wdd);
>   	unsigned long flags;
>
> -	raw_spin_lock_irqsave(&bcm63xx_wdt_device.lock, flags);
> -	bcm_writel(wdt_time * WDT_HZ, bcm63xx_wdt_device.regs + WDT_DEFVAL_REG);
> -	bcm_writel(WDT_START_1, bcm63xx_wdt_device.regs + WDT_CTL_REG);
> -	bcm_writel(WDT_START_2, bcm63xx_wdt_device.regs + WDT_CTL_REG);
> -	bcm63xx_wdt_device.running = true;
> -	raw_spin_unlock_irqrestore(&bcm63xx_wdt_device.lock, flags);
> +	raw_spin_lock_irqsave(&hw->lock, flags);
> +	bcm_writel(wdd->timeout * WDT_HZ, hw->regs + WDT_DEFVAL_REG);
> +	bcm_writel(WDT_START_1, hw->regs + WDT_CTL_REG);
> +	bcm_writel(WDT_START_2, hw->regs + WDT_CTL_REG);
> +	hw->running = true;
> +	raw_spin_unlock_irqrestore(&hw->lock, flags);
> +	return 0;
>   }
>
> -static void bcm63xx_wdt_hw_stop(void)
> +static int bcm63xx_wdt_stop(struct watchdog_device *wdd)
>   {
> +	struct bcm63xx_wdt_hw *hw = watchdog_get_drvdata(wdd);
>   	unsigned long flags;
>
> -	raw_spin_lock_irqsave(&bcm63xx_wdt_device.lock, flags);
> -	bcm_writel(WDT_STOP_1, bcm63xx_wdt_device.regs + WDT_CTL_REG);
> -	bcm_writel(WDT_STOP_2, bcm63xx_wdt_device.regs + WDT_CTL_REG);
> -	bcm63xx_wdt_device.running = false;
> -	raw_spin_unlock_irqrestore(&bcm63xx_wdt_device.lock, flags);
> +	raw_spin_lock_irqsave(&hw->lock, flags);
> +	bcm_writel(WDT_STOP_1, hw->regs + WDT_CTL_REG);
> +	bcm_writel(WDT_STOP_2, hw->regs + WDT_CTL_REG);
> +	hw->running = false;
> +	raw_spin_unlock_irqrestore(&hw->lock, flags);
> +	return 0;
> +}
> +
> +static int bcm63xx_wdt_set_timeout(struct watchdog_device *wdd,
> +	unsigned int timeout)
> +{
> +	wdd->timeout = timeout;
> +	return bcm63xx_wdt_start(wdd);

If I see correctly, there is no ping function. In that case, the watchdog core
will call the start function after updating the timeout, so there is no need
to do it here.

>   }
>
>   /* The watchdog interrupt occurs when half the timeout is remaining */
>   static void bcm63xx_wdt_isr(void *data)
>   {
> -	struct bcm63xx_wdt_hw *hw = &bcm63xx_wdt_device;
> +	struct watchdog_device *wdd = data;
> +	struct bcm63xx_wdt_hw *hw = watchdog_get_drvdata(wdd);
>   	unsigned long flags;
>
>   	raw_spin_lock_irqsave(&hw->lock, flags);
> @@ -118,147 +117,36 @@ static void bcm63xx_wdt_isr(void *data)
>   		}
>
>   		ms = timeleft / (WDT_HZ / 1000);
> -		pr_alert("warning timer fired, reboot in %ums\n", ms);
> +		dev_alert(wdd->dev,
> +			"warning timer fired, reboot in %ums\n", ms);
>   	}
>   	raw_spin_unlock_irqrestore(&hw->lock, flags);
>   }
>
> -static int bcm63xx_wdt_settimeout(int new_time)
> -{
> -	if ((new_time <= 0) || (new_time > WDT_MAX_TIME))
> -		return -EINVAL;
> -
> -	wdt_time = new_time;
> -
> -	return 0;
> -}
> -
> -static int bcm63xx_wdt_open(struct inode *inode, struct file *file)
> -{
> -	if (test_and_set_bit(0, &bcm63xx_wdt_device.inuse))
> -		return -EBUSY;
> -
> -	bcm63xx_wdt_hw_start();
> -	return nonseekable_open(inode, file);
> -}
> -
> -static int bcm63xx_wdt_release(struct inode *inode, struct file *file)
> -{
> -	if (expect_close == 42)
> -		bcm63xx_wdt_hw_stop();
> -	else {
> -		pr_crit("Unexpected close, not stopping watchdog!\n");
> -		bcm63xx_wdt_hw_start();
> -	}
> -	clear_bit(0, &bcm63xx_wdt_device.inuse);
> -	expect_close = 0;
> -	return 0;
> -}
> -
> -static ssize_t bcm63xx_wdt_write(struct file *file, const char *data,
> -				size_t len, loff_t *ppos)
> -{
> -	if (len) {
> -		if (!nowayout) {
> -			size_t i;
> -
> -			/* In case it was set long ago */
> -			expect_close = 0;
> -
> -			for (i = 0; i != len; i++) {
> -				char c;
> -				if (get_user(c, data + i))
> -					return -EFAULT;
> -				if (c == 'V')
> -					expect_close = 42;
> -			}
> -		}
> -		bcm63xx_wdt_hw_start();
> -	}
> -	return len;
> -}
> -
> -static struct watchdog_info bcm63xx_wdt_info = {
> -	.identity       = PFX,
> -	.options        = WDIOF_SETTIMEOUT |
> -				WDIOF_KEEPALIVEPING |
> -				WDIOF_MAGICCLOSE,
> -};
> -
> -
> -static long bcm63xx_wdt_ioctl(struct file *file, unsigned int cmd,
> -				unsigned long arg)
> -{
> -	void __user *argp = (void __user *)arg;
> -	int __user *p = argp;
> -	int new_value, retval = -EINVAL;
> -
> -	switch (cmd) {
> -	case WDIOC_GETSUPPORT:
> -		return copy_to_user(argp, &bcm63xx_wdt_info,
> -			sizeof(bcm63xx_wdt_info)) ? -EFAULT : 0;
> -
> -	case WDIOC_GETSTATUS:
> -	case WDIOC_GETBOOTSTATUS:
> -		return put_user(0, p);
> -
> -	case WDIOC_SETOPTIONS:
> -		if (get_user(new_value, p))
> -			return -EFAULT;
> -
> -		if (new_value & WDIOS_DISABLECARD) {
> -			bcm63xx_wdt_hw_stop();
> -			retval = 0;
> -		}
> -		if (new_value & WDIOS_ENABLECARD) {
> -			bcm63xx_wdt_hw_start();
> -			retval = 0;
> -		}
> -
> -		return retval;
> -
> -	case WDIOC_KEEPALIVE:
> -		bcm63xx_wdt_hw_start();
> -		return 0;
> -
> -	case WDIOC_SETTIMEOUT:
> -		if (get_user(new_value, p))
> -			return -EFAULT;
> -
> -		if (bcm63xx_wdt_settimeout(new_value))
> -			return -EINVAL;
> -
> -		bcm63xx_wdt_hw_start();
> -
> -	case WDIOC_GETTIMEOUT:
> -		return put_user(wdt_time, p);
> -
> -	default:
> -		return -ENOTTY;
> -
> -	}
> -}
> -
> -static const struct file_operations bcm63xx_wdt_fops = {
> -	.owner		= THIS_MODULE,
> -	.llseek		= no_llseek,
> -	.write		= bcm63xx_wdt_write,
> -	.unlocked_ioctl	= bcm63xx_wdt_ioctl,
> -	.open		= bcm63xx_wdt_open,
> -	.release	= bcm63xx_wdt_release,
> +static struct watchdog_ops bcm63xx_wdt_ops = {
> +	.owner = THIS_MODULE,
> +	.start = bcm63xx_wdt_start,
> +	.stop = bcm63xx_wdt_stop,
> +	.set_timeout = bcm63xx_wdt_set_timeout,
>   };
>
> -static struct miscdevice bcm63xx_wdt_miscdev = {
> -	.minor	= WATCHDOG_MINOR,
> -	.name	= "watchdog",
> -	.fops	= &bcm63xx_wdt_fops,
> +static const struct watchdog_info bcm63xx_wdt_info = {
> +	.options = WDIOC_GETTIMELEFT | WDIOF_SETTIMEOUT |

Where is the gettimeleft function ? I think you are adding it with a later patch,
but then you should set the flag there, not here.

> +			WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
> +	.identity = "BCM63xx Watchdog",
>   };
>
> -
>   static int bcm63xx_wdt_probe(struct platform_device *pdev)
>   {
> -	int ret;
> +	struct bcm63xx_wdt_hw *hw;
> +	struct watchdog_device *wdd;
>   	struct resource *r;
> +	int ret;
> +
> +	hw = devm_kzalloc(&pdev->dev, sizeof(*hw), GFP_KERNEL);
> +	wdd = devm_kzalloc(&pdev->dev, sizeof(*wdd), GFP_KERNEL);

It would be better to allocate wdd as part of struct bcm63xx_wdt_hw.
Then you only need a single allocation. You can still use
	wdd = &hw->wdd;
to simplify the rest of this function.

> +	if (!hw || !wdd)
> +		return -ENOMEM;
>
>   	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>   	if (!r) {
> @@ -266,36 +154,44 @@ static int bcm63xx_wdt_probe(struct platform_device *pdev)
>   		return -ENODEV;
>   	}
>
> -	bcm63xx_wdt_device.regs = devm_ioremap_nocache(&pdev->dev, r->start,
> -							resource_size(r));
> -	if (!bcm63xx_wdt_device.regs) {
> +	hw->regs = devm_ioremap_nocache(&pdev->dev, r->start, resource_size(r));
> +	if (!hw->regs) {
>   		dev_err(&pdev->dev, "failed to remap I/O resources\n");
>   		return -ENXIO;
>   	}
>
> -	raw_spin_lock_init(&bcm63xx_wdt_device.lock);
> +	raw_spin_lock_init(&hw->lock);
> +	hw->running = false;
> +
> +	wdd->parent = &pdev->dev;
> +	wdd->ops = &bcm63xx_wdt_ops;
> +	wdd->info = &bcm63xx_wdt_info;
> +	wdd->min_timeout = 1;
> +	wdd->max_timeout = 0xffffffff / WDT_HZ;
> +	wdd->timeout = min(30U, wdd->max_timeout);
> +
> +	watchdog_set_drvdata(wdd, hw);
> +	platform_set_drvdata(pdev, wdd);
> +
> +	watchdog_init_timeout(wdd, 0, &pdev->dev);
> +	watchdog_set_nowayout(wdd, nowayout);
>
> -	ret = bcm63xx_timer_register(TIMER_WDT_ID, bcm63xx_wdt_isr, NULL);
> +	ret = bcm63xx_timer_register(TIMER_WDT_ID, bcm63xx_wdt_isr, wdd);
>   	if (ret < 0) {
>   		dev_err(&pdev->dev, "failed to register wdt timer isr\n");
>   		return ret;
>   	}
>
> -	if (bcm63xx_wdt_settimeout(wdt_time)) {
> -		bcm63xx_wdt_settimeout(WDT_DEFAULT_TIME);
> -		dev_info(&pdev->dev,
> -			": wdt_time value must be 1 <= wdt_time <= %d, using %d\n",
> -			WDT_MAX_TIME, wdt_time);
> -	}
> -
> -	ret = misc_register(&bcm63xx_wdt_miscdev);
> +	ret = watchdog_register_device(wdd);
>   	if (ret < 0) {
>   		dev_err(&pdev->dev, "failed to register watchdog device\n");
>   		goto unregister_timer;
>   	}
>
> -	dev_info(&pdev->dev, " started, timer margin: %d sec\n",
> -						WDT_DEFAULT_TIME);
> +	dev_info(&pdev->dev,
> +		"%s at MMIO 0x%p (timeout = %us, max_timeout = %us)",
> +		dev_name(wdd->dev), hw->regs,
> +		wdd->timeout, wdd->max_timeout);
>
>   	return 0;
>
> @@ -306,17 +202,18 @@ unregister_timer:
>
>   static int bcm63xx_wdt_remove(struct platform_device *pdev)
>   {
> -	if (!nowayout)
> -		bcm63xx_wdt_hw_stop();
> +	struct watchdog_device *wdd = platform_get_drvdata(pdev);
>
> -	misc_deregister(&bcm63xx_wdt_miscdev);
>   	bcm63xx_timer_unregister(TIMER_WDT_ID);
> +	watchdog_unregister_device(wdd);

Shouldn't that come first, before unregistering the timer ?

>   	return 0;
>   }
>
>   static void bcm63xx_wdt_shutdown(struct platform_device *pdev)
>   {
> -	bcm63xx_wdt_hw_stop();
> +	struct watchdog_device *wdd = platform_get_drvdata(pdev);
> +
> +	bcm63xx_wdt_stop(wdd);
>   }
>
>   static struct platform_driver bcm63xx_wdt_driver = {
>

--
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] 95+ messages in thread

* Re: [PATCH (v2) 7/10] watchdog: bcm63xx_wdt: Add get_timeleft function
@ 2015-11-25  2:51                 ` Guenter Roeck
  0 siblings, 0 replies; 95+ messages in thread
From: Guenter Roeck @ 2015-11-25  2:51 UTC (permalink / raw)
  To: Simon Arlott, linux-watchdog
  Cc: devicetree, Ralf Baechle, Thomas Gleixner, Jason Cooper,
	Marc Zyngier, Kevin Cernekee, Florian Fainelli, Wim Van Sebroeck,
	Maxime Bizon, Linux Kernel Mailing List, linux-mips, Rob Herring,
	Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, Jonas Gorski

On 11/24/2015 02:15 PM, Simon Arlott wrote:
> Return the remaining time from the hardware control register.
>
> Warn when the device is registered if the hardware watchdog is currently
> running and report the remaining time left.

This is really two logical changes, isn't it ?

Nice trick to figure out if the watchdog is running.

What is the impact ? Will this result in interrupts ?
If so, would it make sense to _not_ reset the system after a timeout
in this case, but to keep pinging the watchdog while the watchdog device
is not open ?

Thanks,
Guenter

>
> Signed-off-by: Simon Arlott <simon@fire.lp0.eu>
> ---
> Changed "if (timeleft > 0)" to "if (hw->running)" when checking if a
> warning should be printed, in case the time left is truncated down to
> 0 seconds.
>
>   drivers/watchdog/bcm63xx_wdt.c | 37 +++++++++++++++++++++++++++++++++++++
>   1 file changed, 37 insertions(+)
>
> diff --git a/drivers/watchdog/bcm63xx_wdt.c b/drivers/watchdog/bcm63xx_wdt.c
> index 3c7667a..9d099e0 100644
> --- a/drivers/watchdog/bcm63xx_wdt.c
> +++ b/drivers/watchdog/bcm63xx_wdt.c
> @@ -14,6 +14,7 @@
>   #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
>
>   #include <linux/clk.h>
> +#include <linux/delay.h>
>   #include <linux/errno.h>
>   #include <linux/io.h>
>   #include <linux/kernel.h>
> @@ -75,6 +76,19 @@ static int bcm63xx_wdt_stop(struct watchdog_device *wdd)
>   	return 0;
>   }
>
> +static unsigned int bcm63xx_wdt_get_timeleft(struct watchdog_device *wdd)
> +{
> +	struct bcm63xx_wdt_hw *hw = watchdog_get_drvdata(wdd);
> +	unsigned long flags;
> +	u32 val;
> +
> +	raw_spin_lock_irqsave(&hw->lock, flags);
> +	val = __raw_readl(hw->regs + WDT_CTL_REG);
> +	val /= hw->clock_hz;
> +	raw_spin_unlock_irqrestore(&hw->lock, flags);
> +	return val;
> +}
> +
>   static int bcm63xx_wdt_set_timeout(struct watchdog_device *wdd,
>   	unsigned int timeout)
>   {
> @@ -130,6 +144,7 @@ static struct watchdog_ops bcm63xx_wdt_ops = {
>   	.owner = THIS_MODULE,
>   	.start = bcm63xx_wdt_start,
>   	.stop = bcm63xx_wdt_stop,
> +	.get_timeleft = bcm63xx_wdt_get_timeleft,
>   	.set_timeout = bcm63xx_wdt_set_timeout,
>   };
>
> @@ -144,6 +159,8 @@ static int bcm63xx_wdt_probe(struct platform_device *pdev)
>   	struct bcm63xx_wdt_hw *hw;
>   	struct watchdog_device *wdd;
>   	struct resource *r;
> +	u32 timeleft1, timeleft2;
> +	unsigned int timeleft;
>   	int ret;
>
>   	hw = devm_kzalloc(&pdev->dev, sizeof(*hw), GFP_KERNEL);
> @@ -197,6 +214,23 @@ static int bcm63xx_wdt_probe(struct platform_device *pdev)
>   	watchdog_init_timeout(wdd, 0, &pdev->dev);
>   	watchdog_set_nowayout(wdd, nowayout);
>
> +	/* Compare two reads of the time left value, 2 clock ticks apart */
> +	rmb();
> +	timeleft1 = __raw_readl(hw->regs + WDT_CTL_REG);
> +	udelay(DIV_ROUND_UP(1000000, hw->clock_hz / 2));
> +	/* Ensure the register is read twice */
> +	rmb();
> +	timeleft2 = __raw_readl(hw->regs + WDT_CTL_REG);
> +
> +	/* If the time left is changing, the watchdog is running */
> +	if (timeleft1 != timeleft2) {
> +		hw->running = true;
> +		timeleft = bcm63xx_wdt_get_timeleft(wdd);
> +	} else {
> +		hw->running = false;
> +		timeleft = 0;
> +	}
> +
>   	ret = bcm63xx_timer_register(TIMER_WDT_ID, bcm63xx_wdt_isr, wdd);
>   	if (ret < 0) {
>   		dev_err(&pdev->dev, "failed to register wdt timer isr\n");
> @@ -214,6 +248,8 @@ static int bcm63xx_wdt_probe(struct platform_device *pdev)
>   		dev_name(wdd->dev), hw->regs,
>   		wdd->timeout, wdd->max_timeout);
>
> +	if (hw->running)
> +		dev_alert(wdd->dev, "running, reboot in %us\n", timeleft);
>   	return 0;
>
>   unregister_timer:
> @@ -255,6 +291,7 @@ module_platform_driver(bcm63xx_wdt_driver);
>
>   MODULE_AUTHOR("Miguel Gaio <miguel.gaio@efixo.com>");
>   MODULE_AUTHOR("Florian Fainelli <florian@openwrt.org>");
> +MODULE_AUTHOR("Simon Arlott");
>   MODULE_DESCRIPTION("Driver for the Broadcom BCM63xx SoC watchdog");
>   MODULE_LICENSE("GPL");
>   MODULE_ALIAS("platform:bcm63xx-wdt");
>


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

* Re: [PATCH (v2) 7/10] watchdog: bcm63xx_wdt: Add get_timeleft function
@ 2015-11-25  2:51                 ` Guenter Roeck
  0 siblings, 0 replies; 95+ messages in thread
From: Guenter Roeck @ 2015-11-25  2:51 UTC (permalink / raw)
  To: Simon Arlott, linux-watchdog-u79uwXL29TY76Z2rM5mHXA
  Cc: devicetree-u79uwXL29TY76Z2rM5mHXA, Ralf Baechle, Thomas Gleixner,
	Jason Cooper, Marc Zyngier, Kevin Cernekee, Florian Fainelli,
	Wim Van Sebroeck, Maxime Bizon, Linux Kernel Mailing List,
	linux-mips-6z/3iImG2C8G8FEW9MqTrA, Rob Herring, Pawel Moll,
	Mark Rutland, Ian Campbell, Kumar Gala, Jonas Gorski

On 11/24/2015 02:15 PM, Simon Arlott wrote:
> Return the remaining time from the hardware control register.
>
> Warn when the device is registered if the hardware watchdog is currently
> running and report the remaining time left.

This is really two logical changes, isn't it ?

Nice trick to figure out if the watchdog is running.

What is the impact ? Will this result in interrupts ?
If so, would it make sense to _not_ reset the system after a timeout
in this case, but to keep pinging the watchdog while the watchdog device
is not open ?

Thanks,
Guenter

>
> Signed-off-by: Simon Arlott <simon-A6De1vDTPLDsq35pWSNszA@public.gmane.org>
> ---
> Changed "if (timeleft > 0)" to "if (hw->running)" when checking if a
> warning should be printed, in case the time left is truncated down to
> 0 seconds.
>
>   drivers/watchdog/bcm63xx_wdt.c | 37 +++++++++++++++++++++++++++++++++++++
>   1 file changed, 37 insertions(+)
>
> diff --git a/drivers/watchdog/bcm63xx_wdt.c b/drivers/watchdog/bcm63xx_wdt.c
> index 3c7667a..9d099e0 100644
> --- a/drivers/watchdog/bcm63xx_wdt.c
> +++ b/drivers/watchdog/bcm63xx_wdt.c
> @@ -14,6 +14,7 @@
>   #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
>
>   #include <linux/clk.h>
> +#include <linux/delay.h>
>   #include <linux/errno.h>
>   #include <linux/io.h>
>   #include <linux/kernel.h>
> @@ -75,6 +76,19 @@ static int bcm63xx_wdt_stop(struct watchdog_device *wdd)
>   	return 0;
>   }
>
> +static unsigned int bcm63xx_wdt_get_timeleft(struct watchdog_device *wdd)
> +{
> +	struct bcm63xx_wdt_hw *hw = watchdog_get_drvdata(wdd);
> +	unsigned long flags;
> +	u32 val;
> +
> +	raw_spin_lock_irqsave(&hw->lock, flags);
> +	val = __raw_readl(hw->regs + WDT_CTL_REG);
> +	val /= hw->clock_hz;
> +	raw_spin_unlock_irqrestore(&hw->lock, flags);
> +	return val;
> +}
> +
>   static int bcm63xx_wdt_set_timeout(struct watchdog_device *wdd,
>   	unsigned int timeout)
>   {
> @@ -130,6 +144,7 @@ static struct watchdog_ops bcm63xx_wdt_ops = {
>   	.owner = THIS_MODULE,
>   	.start = bcm63xx_wdt_start,
>   	.stop = bcm63xx_wdt_stop,
> +	.get_timeleft = bcm63xx_wdt_get_timeleft,
>   	.set_timeout = bcm63xx_wdt_set_timeout,
>   };
>
> @@ -144,6 +159,8 @@ static int bcm63xx_wdt_probe(struct platform_device *pdev)
>   	struct bcm63xx_wdt_hw *hw;
>   	struct watchdog_device *wdd;
>   	struct resource *r;
> +	u32 timeleft1, timeleft2;
> +	unsigned int timeleft;
>   	int ret;
>
>   	hw = devm_kzalloc(&pdev->dev, sizeof(*hw), GFP_KERNEL);
> @@ -197,6 +214,23 @@ static int bcm63xx_wdt_probe(struct platform_device *pdev)
>   	watchdog_init_timeout(wdd, 0, &pdev->dev);
>   	watchdog_set_nowayout(wdd, nowayout);
>
> +	/* Compare two reads of the time left value, 2 clock ticks apart */
> +	rmb();
> +	timeleft1 = __raw_readl(hw->regs + WDT_CTL_REG);
> +	udelay(DIV_ROUND_UP(1000000, hw->clock_hz / 2));
> +	/* Ensure the register is read twice */
> +	rmb();
> +	timeleft2 = __raw_readl(hw->regs + WDT_CTL_REG);
> +
> +	/* If the time left is changing, the watchdog is running */
> +	if (timeleft1 != timeleft2) {
> +		hw->running = true;
> +		timeleft = bcm63xx_wdt_get_timeleft(wdd);
> +	} else {
> +		hw->running = false;
> +		timeleft = 0;
> +	}
> +
>   	ret = bcm63xx_timer_register(TIMER_WDT_ID, bcm63xx_wdt_isr, wdd);
>   	if (ret < 0) {
>   		dev_err(&pdev->dev, "failed to register wdt timer isr\n");
> @@ -214,6 +248,8 @@ static int bcm63xx_wdt_probe(struct platform_device *pdev)
>   		dev_name(wdd->dev), hw->regs,
>   		wdd->timeout, wdd->max_timeout);
>
> +	if (hw->running)
> +		dev_alert(wdd->dev, "running, reboot in %us\n", timeleft);
>   	return 0;
>
>   unregister_timer:
> @@ -255,6 +291,7 @@ module_platform_driver(bcm63xx_wdt_driver);
>
>   MODULE_AUTHOR("Miguel Gaio <miguel.gaio-HH44TBFINEIAvxtiuMwx3w@public.gmane.org>");
>   MODULE_AUTHOR("Florian Fainelli <florian-p3rKhJxN3npAfugRpC6u6w@public.gmane.org>");
> +MODULE_AUTHOR("Simon Arlott");
>   MODULE_DESCRIPTION("Driver for the Broadcom BCM63xx SoC watchdog");
>   MODULE_LICENSE("GPL");
>   MODULE_ALIAS("platform:bcm63xx-wdt");
>

--
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] 95+ messages in thread

* Re: [PATCH (v2) 1/10] clocksource: Add brcm,bcm6345-timer device tree binding
  2015-11-23 18:55     ` Simon Arlott
  (?)
  (?)
@ 2015-11-25  3:05     ` Rob Herring
  -1 siblings, 0 replies; 95+ messages in thread
From: Rob Herring @ 2015-11-25  3:05 UTC (permalink / raw)
  To: Simon Arlott
  Cc: Jonas Gorski, devicetree, Ralf Baechle, Thomas Gleixner,
	Jason Cooper, Marc Zyngier, Kevin Cernekee, Florian Fainelli,
	Wim Van Sebroeck, Miguel Gaio, Maxime Bizon,
	Linux Kernel Mailing List, MIPS Mailing List, linux-watchdog,
	Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala

On Mon, Nov 23, 2015 at 06:55:38PM +0000, Simon Arlott wrote:
> Add device tree bindings for the BCM6345/BCM6318 timers. This is required
> for the BCM6345 watchdog which needs to respond to one of the timer
> interrupts.
> 
> Signed-off-by: Simon Arlott <simon@fire.lp0.eu>

Acked-by: Rob Herring <robh@kernel.org>

> ---
> On 23/11/15 15:33, Jonas Gorski wrote:
> > On Sat, Nov 21, 2015 at 8:02 PM, Simon Arlott <simon@fire.lp0.eu> wrote:
> >> +- compatible: should be "brcm,bcm<soc>-timer", "brcm,bcm6345-timer"
> > 
> > Since bcm6318 uses a slightly different register layout than the
> > earlier SoCs, I'd argue that using bcm6345-timer as a compatible for
> > bcm6318 is wrong.
> 
> I've split them out into two very similar bindings.
> 
> Patches 1/4 and 2/4 are replaced with (v2) 1/10 and (v2) 2/10.
> 
>  .../bindings/timer/brcm,bcm6318-timer.txt          | 44 ++++++++++++++++++++
>  .../bindings/timer/brcm,bcm6345-timer.txt          | 47 ++++++++++++++++++++++
>  2 files changed, 91 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/timer/brcm,bcm6318-timer.txt
>  create mode 100644 Documentation/devicetree/bindings/timer/brcm,bcm6345-timer.txt
> 
> diff --git a/Documentation/devicetree/bindings/timer/brcm,bcm6318-timer.txt b/Documentation/devicetree/bindings/timer/brcm,bcm6318-timer.txt
> new file mode 100644
> index 0000000..cf4be7e
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/timer/brcm,bcm6318-timer.txt
> @@ -0,0 +1,44 @@
> +Broadcom BCM6318 Timer
> +
> +This block is a timer that is connected to multiple interrupts on the main
> +interrupt controller and functions as a programmable interrupt controller for
> +timer events. There is a main timer interrupt for all timers.
> +
> +- 4 independent timers with their own interrupt, and own maskable level
> +  interrupt bit in the main timer interrupt
> +
> +- 1 watchdog timer with an unmaskable level interrupt bit in the main timer
> +  interrupt
> +
> +- Contains one enable/status word pair
> +
> +- No atomic set/clear operations
> +
> +Required properties:
> +
> +- compatible: should be "brcm,bcm<soc>-timer", "brcm,bcm6318-timer"
> +- reg: specifies the base physical address and size of the registers, excluding
> +  the watchdog registers
> +- interrupt-controller: identifies the node as an interrupt controller
> +- #interrupt-cells: specifies the number of cells needed to encode an interrupt
> +  source, should be 1.
> +- interrupt-parent: specifies the phandle to the parent interrupt controller(s)
> +  this one is cascaded from
> +- interrupts: specifies the interrupt line(s) in the interrupt-parent controller
> +  node for the main timer interrupt, followed by the individual timer
> +  interrupts; valid values depend on the type of parent interrupt controller
> +- clocks: phandle of timer reference clock (periph)
> +
> +Example:
> +
> +timer: timer@10000040 {
> +	compatible = "brcm,bcm63148-timer", "brcm,bcm6318-timer";
> +	reg = <0x10000040 0x28>;
> +
> +	interrupt-controller;
> +	#interrupt-cells = <1>;
> +
> +	interrupt-parent = <&periph_intc>;
> +	interrupts = <31>, <0>, <1>, <2>, <3>;
> +	clock = <&periph_osc>;
> +};
> diff --git a/Documentation/devicetree/bindings/timer/brcm,bcm6345-timer.txt b/Documentation/devicetree/bindings/timer/brcm,bcm6345-timer.txt
> new file mode 100644
> index 0000000..03250dd
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/timer/brcm,bcm6345-timer.txt
> @@ -0,0 +1,47 @@
> +Broadcom BCM6345 Timer
> +
> +This block is a timer that is connected to one interrupt on the main interrupt
> +controller and functions as a programmable interrupt controller for timer
> +events.
> +
> +- 3 independent timers with their own maskable level interrupt bit (but not
> +  per CPU because there is only one parent interrupt and the timers share it)
> +
> +- 1 watchdog timer with an unmaskable level interrupt
> +
> +- Contains one enable/status word pair
> +
> +- No atomic set/clear operations
> +
> +The lack of per CPU ability of timers makes them unusable as a set of
> +clockevent devices, otherwise they could be attached to the remaining
> +interrupts.
> +
> +Required properties:
> +
> +- compatible: should be "brcm,bcm<soc>-timer", "brcm,bcm6345-timer"
> +- reg: specifies the base physical address and size of the registers, excluding
> +  the watchdog registers
> +- interrupt-controller: identifies the node as an interrupt controller
> +- #interrupt-cells: specifies the number of cells needed to encode an interrupt
> +  source, should be 1.
> +- interrupt-parent: specifies the phandle to the parent interrupt controller(s)
> +  this one is cascaded from
> +- interrupts: specifies the interrupt line(s) in the interrupt-parent controller
> +  node for the timer interrupt; valid values depend on the type of parent
> +  interrupt controller
> +- clocks: phandle of timer reference clock (periph)
> +
> +Example:
> +
> +timer: timer@10000080 {
> +	compatible = "brcm,bcm63168-timer", "brcm,bcm6345-timer";
> +	reg = <0x10000080 0x1c>;
> +
> +	interrupt-controller;
> +	#interrupt-cells = <1>;
> +
> +	interrupt-parent = <&periph_intc>;
> +	interrupts = <0>;
> +	clock·=·<&periph_osc>;
> +};
> -- 
> 2.1.4
> 
> -- 
> Simon Arlott

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

* Re: [PATCH (v2) 7/10] watchdog: bcm63xx_wdt: Add get_timeleft function
@ 2015-11-25  8:17                   ` Simon Arlott
  0 siblings, 0 replies; 95+ messages in thread
From: Simon Arlott @ 2015-11-25  8:17 UTC (permalink / raw)
  To: Guenter Roeck, linux-watchdog
  Cc: devicetree, Ralf Baechle, Thomas Gleixner, Jason Cooper,
	Marc Zyngier, Kevin Cernekee, Florian Fainelli, Wim Van Sebroeck,
	Maxime Bizon, Linux Kernel Mailing List, linux-mips, Rob Herring,
	Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, Jonas Gorski

On 25/11/15 02:51, Guenter Roeck wrote:
> On 11/24/2015 02:15 PM, Simon Arlott wrote:
>> Return the remaining time from the hardware control register.
>>
>> Warn when the device is registered if the hardware watchdog is currently
>> running and report the remaining time left.
> 
> This is really two logical changes, isn't it ?

If you insist then I'll split it out into yet another patch.

> Nice trick to figure out if the watchdog is running.
> 
> What is the impact ? Will this result in interrupts ?

Yes, if it is running it will receive interrupts and check hw->running
to determine if it should stop the watchdog or not.

> If so, would it make sense to _not_ reset the system after a timeout
> in this case, but to keep pinging the watchdog while the watchdog device
> is not open ?

As the whole point of a hardware watchdog is to reset the system when
there is a problem with the software, it should not be automatically
reset by the driver on startup. If the watchdog is already running then
it needs to be pinged by userspace before the timeout.

The bootloader (CFE) doesn't leave the watchdog running. On my system I
prepend some code before vmlinuz that starts it running at the maximum
timeout.

A module parameter could be added to automatically ping/stop it if it's
running, but this should be in the watchdog core and not an individual
driver.

-- 
Simon Arlott

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

* Re: [PATCH (v2) 7/10] watchdog: bcm63xx_wdt: Add get_timeleft function
@ 2015-11-25  8:17                   ` Simon Arlott
  0 siblings, 0 replies; 95+ messages in thread
From: Simon Arlott @ 2015-11-25  8:17 UTC (permalink / raw)
  To: Guenter Roeck, linux-watchdog-u79uwXL29TY76Z2rM5mHXA
  Cc: devicetree-u79uwXL29TY76Z2rM5mHXA, Ralf Baechle, Thomas Gleixner,
	Jason Cooper, Marc Zyngier, Kevin Cernekee, Florian Fainelli,
	Wim Van Sebroeck, Maxime Bizon, Linux Kernel Mailing List,
	linux-mips-6z/3iImG2C8G8FEW9MqTrA, Rob Herring, Pawel Moll,
	Mark Rutland, Ian Campbell, Kumar Gala, Jonas Gorski

On 25/11/15 02:51, Guenter Roeck wrote:
> On 11/24/2015 02:15 PM, Simon Arlott wrote:
>> Return the remaining time from the hardware control register.
>>
>> Warn when the device is registered if the hardware watchdog is currently
>> running and report the remaining time left.
> 
> This is really two logical changes, isn't it ?

If you insist then I'll split it out into yet another patch.

> Nice trick to figure out if the watchdog is running.
> 
> What is the impact ? Will this result in interrupts ?

Yes, if it is running it will receive interrupts and check hw->running
to determine if it should stop the watchdog or not.

> If so, would it make sense to _not_ reset the system after a timeout
> in this case, but to keep pinging the watchdog while the watchdog device
> is not open ?

As the whole point of a hardware watchdog is to reset the system when
there is a problem with the software, it should not be automatically
reset by the driver on startup. If the watchdog is already running then
it needs to be pinged by userspace before the timeout.

The bootloader (CFE) doesn't leave the watchdog running. On my system I
prepend some code before vmlinuz that starts it running at the maximum
timeout.

A module parameter could be added to automatically ping/stop it if it's
running, but this should be in the watchdog core and not an individual
driver.

-- 
Simon Arlott
--
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] 95+ messages in thread

* Re: [PATCH 5/10] watchdog: bcm63xx_wdt: Use WATCHDOG_CORE
  2015-11-25  2:44               ` Guenter Roeck
  (?)
@ 2015-11-25 13:02               ` Simon Arlott
  2015-11-25 14:10                   ` Guenter Roeck
  -1 siblings, 1 reply; 95+ messages in thread
From: Simon Arlott @ 2015-11-25 13:02 UTC (permalink / raw)
  To: Guenter Roeck
  Cc: devicetree, Ralf Baechle, Thomas Gleixner, Jason Cooper,
	Marc Zyngier, Kevin Cernekee, Florian Fainelli, Wim Van Sebroeck,
	Maxime Bizon, Linux Kernel Mailing List, linux-mips,
	linux-watchdog, Rob Herring, Pawel Moll, Mark Rutland,
	Ian Campbell, Kumar Gala, Jonas Gorski

On Wed, November 25, 2015 02:44, Guenter Roeck wrote:
> The "running" flag should no longer be needed. watchdog_active()
> should provide that information.

I'm going to need to keep that because I need to know if it's running
in the interrupt handler, and wdd->lock is a mutex.

>> @@ -306,17 +202,18 @@ unregister_timer:
>>
>>   static int bcm63xx_wdt_remove(struct platform_device *pdev)
>>   {
>> -	if (!nowayout)
>> -		bcm63xx_wdt_hw_stop();
>> +	struct watchdog_device *wdd = platform_get_drvdata(pdev);
>>
>> -	misc_deregister(&bcm63xx_wdt_miscdev);
>>   	bcm63xx_timer_unregister(TIMER_WDT_ID);
>> +	watchdog_unregister_device(wdd);
>
> Shouldn't that come first, before unregistering the timer ?

No, because wdd->dev is used in the interrupt handler. I will have to
move registration of the interrupt to after creating the watchdog
because it could currently be used before wdd->dev is set.

-- 
Simon Arlott

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

* Re: [PATCH 5/10] watchdog: bcm63xx_wdt: Use WATCHDOG_CORE
@ 2015-11-25 14:10                   ` Guenter Roeck
  0 siblings, 0 replies; 95+ messages in thread
From: Guenter Roeck @ 2015-11-25 14:10 UTC (permalink / raw)
  To: Simon Arlott
  Cc: devicetree, Ralf Baechle, Thomas Gleixner, Jason Cooper,
	Marc Zyngier, Kevin Cernekee, Florian Fainelli, Wim Van Sebroeck,
	Maxime Bizon, Linux Kernel Mailing List, linux-mips,
	linux-watchdog, Rob Herring, Pawel Moll, Mark Rutland,
	Ian Campbell, Kumar Gala, Jonas Gorski

On 11/25/2015 05:02 AM, Simon Arlott wrote:
> On Wed, November 25, 2015 02:44, Guenter Roeck wrote:
>> The "running" flag should no longer be needed. watchdog_active()
>> should provide that information.
>
> I'm going to need to keep that because I need to know if it's running
> in the interrupt handler, and wdd->lock is a mutex.
>
>>> @@ -306,17 +202,18 @@ unregister_timer:
>>>
>>>    static int bcm63xx_wdt_remove(struct platform_device *pdev)
>>>    {
>>> -	if (!nowayout)
>>> -		bcm63xx_wdt_hw_stop();
>>> +	struct watchdog_device *wdd = platform_get_drvdata(pdev);
>>>
>>> -	misc_deregister(&bcm63xx_wdt_miscdev);
>>>    	bcm63xx_timer_unregister(TIMER_WDT_ID);
>>> +	watchdog_unregister_device(wdd);
>>
>> Shouldn't that come first, before unregistering the timer ?
>
> No, because wdd->dev is used in the interrupt handler. I will have to
> move registration of the interrupt to after creating the watchdog
> because it could currently be used before wdd->dev is set.
>

Does unregistering the timer disable the interrupt ?

Thanks,
Guenter


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

* Re: [PATCH 5/10] watchdog: bcm63xx_wdt: Use WATCHDOG_CORE
@ 2015-11-25 14:10                   ` Guenter Roeck
  0 siblings, 0 replies; 95+ messages in thread
From: Guenter Roeck @ 2015-11-25 14:10 UTC (permalink / raw)
  To: Simon Arlott
  Cc: devicetree-u79uwXL29TY76Z2rM5mHXA, Ralf Baechle, Thomas Gleixner,
	Jason Cooper, Marc Zyngier, Kevin Cernekee, Florian Fainelli,
	Wim Van Sebroeck, Maxime Bizon, Linux Kernel Mailing List,
	linux-mips-6z/3iImG2C8G8FEW9MqTrA,
	linux-watchdog-u79uwXL29TY76Z2rM5mHXA, Rob Herring, Pawel Moll,
	Mark Rutland, Ian Campbell, Kumar Gala, Jonas Gorski

On 11/25/2015 05:02 AM, Simon Arlott wrote:
> On Wed, November 25, 2015 02:44, Guenter Roeck wrote:
>> The "running" flag should no longer be needed. watchdog_active()
>> should provide that information.
>
> I'm going to need to keep that because I need to know if it's running
> in the interrupt handler, and wdd->lock is a mutex.
>
>>> @@ -306,17 +202,18 @@ unregister_timer:
>>>
>>>    static int bcm63xx_wdt_remove(struct platform_device *pdev)
>>>    {
>>> -	if (!nowayout)
>>> -		bcm63xx_wdt_hw_stop();
>>> +	struct watchdog_device *wdd = platform_get_drvdata(pdev);
>>>
>>> -	misc_deregister(&bcm63xx_wdt_miscdev);
>>>    	bcm63xx_timer_unregister(TIMER_WDT_ID);
>>> +	watchdog_unregister_device(wdd);
>>
>> Shouldn't that come first, before unregistering the timer ?
>
> No, because wdd->dev is used in the interrupt handler. I will have to
> move registration of the interrupt to after creating the watchdog
> because it could currently be used before wdd->dev is set.
>

Does unregistering the timer disable the interrupt ?

Thanks,
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] 95+ messages in thread

* Re: [PATCH 5/10] watchdog: bcm63xx_wdt: Use WATCHDOG_CORE
@ 2015-11-25 19:43                     ` Simon Arlott
  0 siblings, 0 replies; 95+ messages in thread
From: Simon Arlott @ 2015-11-25 19:43 UTC (permalink / raw)
  To: Guenter Roeck
  Cc: devicetree, Ralf Baechle, Thomas Gleixner, Jason Cooper,
	Marc Zyngier, Kevin Cernekee, Florian Fainelli, Wim Van Sebroeck,
	Maxime Bizon, Linux Kernel Mailing List, linux-mips,
	linux-watchdog, Rob Herring, Pawel Moll, Mark Rutland,
	Ian Campbell, Kumar Gala, Jonas Gorski

On 25/11/15 14:10, Guenter Roeck wrote:
> On 11/25/2015 05:02 AM, Simon Arlott wrote:
>> On Wed, November 25, 2015 02:44, Guenter Roeck wrote:
>>> The "running" flag should no longer be needed. watchdog_active()
>>> should provide that information.
>>
>> I'm going to need to keep that because I need to know if it's running
>> in the interrupt handler, and wdd->lock is a mutex.
>>
>>>> @@ -306,17 +202,18 @@ unregister_timer:
>>>>
>>>>    static int bcm63xx_wdt_remove(struct platform_device *pdev)
>>>>    {
>>>> -	if (!nowayout)
>>>> -		bcm63xx_wdt_hw_stop();
>>>> +	struct watchdog_device *wdd = platform_get_drvdata(pdev);
>>>>
>>>> -	misc_deregister(&bcm63xx_wdt_miscdev);
>>>>    	bcm63xx_timer_unregister(TIMER_WDT_ID);
>>>> +	watchdog_unregister_device(wdd);
>>>
>>> Shouldn't that come first, before unregistering the timer ?
>>
>> No, because wdd->dev is used in the interrupt handler. I will have to
>> move registration of the interrupt to after creating the watchdog
>> because it could currently be used before wdd->dev is set.
>>
> 
> Does unregistering the timer disable the interrupt ?

No, it sets the callback for that timer to NULL so that it won't be
called.

-- 
Simon Arlott

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

* Re: [PATCH 5/10] watchdog: bcm63xx_wdt: Use WATCHDOG_CORE
@ 2015-11-25 19:43                     ` Simon Arlott
  0 siblings, 0 replies; 95+ messages in thread
From: Simon Arlott @ 2015-11-25 19:43 UTC (permalink / raw)
  To: Guenter Roeck
  Cc: devicetree-u79uwXL29TY76Z2rM5mHXA, Ralf Baechle, Thomas Gleixner,
	Jason Cooper, Marc Zyngier, Kevin Cernekee, Florian Fainelli,
	Wim Van Sebroeck, Maxime Bizon, Linux Kernel Mailing List,
	linux-mips-6z/3iImG2C8G8FEW9MqTrA,
	linux-watchdog-u79uwXL29TY76Z2rM5mHXA, Rob Herring, Pawel Moll,
	Mark Rutland, Ian Campbell, Kumar Gala, Jonas Gorski

On 25/11/15 14:10, Guenter Roeck wrote:
> On 11/25/2015 05:02 AM, Simon Arlott wrote:
>> On Wed, November 25, 2015 02:44, Guenter Roeck wrote:
>>> The "running" flag should no longer be needed. watchdog_active()
>>> should provide that information.
>>
>> I'm going to need to keep that because I need to know if it's running
>> in the interrupt handler, and wdd->lock is a mutex.
>>
>>>> @@ -306,17 +202,18 @@ unregister_timer:
>>>>
>>>>    static int bcm63xx_wdt_remove(struct platform_device *pdev)
>>>>    {
>>>> -	if (!nowayout)
>>>> -		bcm63xx_wdt_hw_stop();
>>>> +	struct watchdog_device *wdd = platform_get_drvdata(pdev);
>>>>
>>>> -	misc_deregister(&bcm63xx_wdt_miscdev);
>>>>    	bcm63xx_timer_unregister(TIMER_WDT_ID);
>>>> +	watchdog_unregister_device(wdd);
>>>
>>> Shouldn't that come first, before unregistering the timer ?
>>
>> No, because wdd->dev is used in the interrupt handler. I will have to
>> move registration of the interrupt to after creating the watchdog
>> because it could currently be used before wdd->dev is set.
>>
> 
> Does unregistering the timer disable the interrupt ?

No, it sets the callback for that timer to NULL so that it won't be
called.

-- 
Simon Arlott
--
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	[flat|nested] 95+ messages in thread

* Re: [PATCH 4/10] watchdog: bcm63xx_wdt: Handle hardware interrupt and remove software timer
@ 2015-11-25 20:14                 ` Jonas Gorski
  0 siblings, 0 replies; 95+ messages in thread
From: Jonas Gorski @ 2015-11-25 20:14 UTC (permalink / raw)
  To: Guenter Roeck
  Cc: Simon Arlott, Florian Fainelli, devicetree, Ralf Baechle,
	Thomas Gleixner, Jason Cooper, Marc Zyngier, Kevin Cernekee,
	Wim Van Sebroeck, Maxime Bizon, Linux Kernel Mailing List,
	MIPS Mailing List, linux-watchdog, Rob Herring, Pawel Moll,
	Mark Rutland, Ian Campbell, Kumar Gala

On Tue, Nov 24, 2015 at 7:21 PM, Guenter Roeck <linux@roeck-us.net> wrote:
> On Sun, Nov 22, 2015 at 02:05:16PM +0000, Simon Arlott wrote:
>> There is a level triggered interrupt for the watchdog timer as part of
>> the bcm63xx_timer device. The interrupt occurs when the hardware watchdog
>> timer reaches 50% of the remaining time.
>>
>> It is not possible to mask the interrupt within the bcm63xx_timer device.
>> To get around this limitation, handle the interrupt by restarting the
>> watchdog with the current remaining time (which will be half the previous
>> timeout) so that the interrupt occurs again at 1/4th, 1/8th, etc. of the
>> original timeout value until the watchdog forces a reboot.
>>
>> The software timer was restarting the hardware watchdog with a 85 second
>> timeout until the software timer expired, and then causing a panic()
>> about 42.5 seconds later when the hardware interrupt occurred. The
>> hardware watchdog would not reboot until a further 42.5 seconds had
>> passed.
>>
>> Remove the software timer and rely on the hardware timer directly,
>> reducing the maximum timeout from 256 seconds to 85 seconds
>> (2^32 / WDT_HZ).
>>
>
> Florian,
>
> can you have a look into this patch and confirm that there is no better
> way to clear the interrupt status ?

While the watchdog interrupt can't be masked, it should be able to be
cleared by writing 1 to the appropriate bit in the timer block's
interrupt status register. At least the broadcom sources do so.


Jonas

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

* Re: [PATCH 4/10] watchdog: bcm63xx_wdt: Handle hardware interrupt and remove software timer
@ 2015-11-25 20:14                 ` Jonas Gorski
  0 siblings, 0 replies; 95+ messages in thread
From: Jonas Gorski @ 2015-11-25 20:14 UTC (permalink / raw)
  To: Guenter Roeck
  Cc: Simon Arlott, Florian Fainelli,
	devicetree-u79uwXL29TY76Z2rM5mHXA, Ralf Baechle, Thomas Gleixner,
	Jason Cooper, Marc Zyngier, Kevin Cernekee, Wim Van Sebroeck,
	Maxime Bizon, Linux Kernel Mailing List, MIPS Mailing List,
	linux-watchdog-u79uwXL29TY76Z2rM5mHXA, Rob Herring, Pawel Moll,
	Mark Rutland, Ian Campbell, Kumar Gala

On Tue, Nov 24, 2015 at 7:21 PM, Guenter Roeck <linux-0h96xk9xTtrk1uMJSBkQmQ@public.gmane.org> wrote:
> On Sun, Nov 22, 2015 at 02:05:16PM +0000, Simon Arlott wrote:
>> There is a level triggered interrupt for the watchdog timer as part of
>> the bcm63xx_timer device. The interrupt occurs when the hardware watchdog
>> timer reaches 50% of the remaining time.
>>
>> It is not possible to mask the interrupt within the bcm63xx_timer device.
>> To get around this limitation, handle the interrupt by restarting the
>> watchdog with the current remaining time (which will be half the previous
>> timeout) so that the interrupt occurs again at 1/4th, 1/8th, etc. of the
>> original timeout value until the watchdog forces a reboot.
>>
>> The software timer was restarting the hardware watchdog with a 85 second
>> timeout until the software timer expired, and then causing a panic()
>> about 42.5 seconds later when the hardware interrupt occurred. The
>> hardware watchdog would not reboot until a further 42.5 seconds had
>> passed.
>>
>> Remove the software timer and rely on the hardware timer directly,
>> reducing the maximum timeout from 256 seconds to 85 seconds
>> (2^32 / WDT_HZ).
>>
>
> Florian,
>
> can you have a look into this patch and confirm that there is no better
> way to clear the interrupt status ?

While the watchdog interrupt can't be masked, it should be able to be
cleared by writing 1 to the appropriate bit in the timer block's
interrupt status register. At least the broadcom sources do so.


Jonas
--
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	[flat|nested] 95+ messages in thread

* Re: [PATCH 4/10] watchdog: bcm63xx_wdt: Handle hardware interrupt and remove software timer
@ 2015-11-25 20:28                   ` Simon Arlott
  0 siblings, 0 replies; 95+ messages in thread
From: Simon Arlott @ 2015-11-25 20:28 UTC (permalink / raw)
  To: Jonas Gorski, Guenter Roeck
  Cc: Florian Fainelli, devicetree, Ralf Baechle, Thomas Gleixner,
	Jason Cooper, Marc Zyngier, Kevin Cernekee, Wim Van Sebroeck,
	Maxime Bizon, Linux Kernel Mailing List, MIPS Mailing List,
	linux-watchdog, Rob Herring, Pawel Moll, Mark Rutland,
	Ian Campbell, Kumar Gala

On 25/11/15 20:14, Jonas Gorski wrote:
> On Tue, Nov 24, 2015 at 7:21 PM, Guenter Roeck <linux@roeck-us.net> wrote:
>> On Sun, Nov 22, 2015 at 02:05:16PM +0000, Simon Arlott wrote:
>>> There is a level triggered interrupt for the watchdog timer as part of
>>> the bcm63xx_timer device. The interrupt occurs when the hardware watchdog
>>> timer reaches 50% of the remaining time.
>>>
>>> It is not possible to mask the interrupt within the bcm63xx_timer device.
>>> To get around this limitation, handle the interrupt by restarting the
>>> watchdog with the current remaining time (which will be half the previous
>>> timeout) so that the interrupt occurs again at 1/4th, 1/8th, etc. of the
>>> original timeout value until the watchdog forces a reboot.
>>>
>>> The software timer was restarting the hardware watchdog with a 85 second
>>> timeout until the software timer expired, and then causing a panic()
>>> about 42.5 seconds later when the hardware interrupt occurred. The
>>> hardware watchdog would not reboot until a further 42.5 seconds had
>>> passed.
>>>
>>> Remove the software timer and rely on the hardware timer directly,
>>> reducing the maximum timeout from 256 seconds to 85 seconds
>>> (2^32 / WDT_HZ).
>>>
>>
>> Florian,
>>
>> can you have a look into this patch and confirm that there is no better
>> way to clear the interrupt status ?
> 
> While the watchdog interrupt can't be masked, it should be able to be
> cleared by writing 1 to the appropriate bit in the timer block's
> interrupt status register. At least the broadcom sources do so.

Not according to the hardware itself:
[    6.674626] watchdog watchdog0: warning timer fired, reboot in 7499ms
[    6.681212] irq_bcm6345_l2_timer: bcm6345_timer_write_int_status: b0000083=08
[    6.688583] watchdog watchdog0: warning timer fired, reboot in 7486ms
[    6.695181] irq_bcm6345_l2_timer: bcm6345_timer_write_int_status: b0000083=08
[    6.702554] watchdog watchdog0: warning timer fired, reboot in 7472ms
[    6.709158] irq_bcm6345_l2_timer: bcm6345_timer_write_int_status: b0000083=08
[    6.716529] watchdog watchdog0: warning timer fired, reboot in 7458ms
[    6.723135] irq_bcm6345_l2_timer: bcm6345_timer_write_int_status: b0000083=08
[    6.730538] watchdog watchdog0: warning timer fired, reboot in 7444ms
[    6.737121] irq_bcm6345_l2_timer: bcm6345_timer_write_int_status: b0000083=08
[    6.744482] watchdog watchdog0: warning timer fired, reboot in 7430ms
[    6.751090] irq_bcm6345_l2_timer: bcm6345_timer_write_int_status: b0000083=08


typedef struct Timer {
    uint16        unused0;
    byte          TimerMask;
#define TIMER0EN        0x01
#define TIMER1EN        0x02
#define TIMER2EN        0x04
    byte          TimerInts;
#define TIMER0          0x01
#define TIMER1          0x02
#define TIMER2          0x04
#define WATCHDOG        0x08
...

-- 
Simon Arlott

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

* Re: [PATCH 4/10] watchdog: bcm63xx_wdt: Handle hardware interrupt and remove software timer
@ 2015-11-25 20:28                   ` Simon Arlott
  0 siblings, 0 replies; 95+ messages in thread
From: Simon Arlott @ 2015-11-25 20:28 UTC (permalink / raw)
  To: Jonas Gorski, Guenter Roeck
  Cc: Florian Fainelli, devicetree-u79uwXL29TY76Z2rM5mHXA,
	Ralf Baechle, Thomas Gleixner, Jason Cooper, Marc Zyngier,
	Kevin Cernekee, Wim Van Sebroeck, Maxime Bizon,
	Linux Kernel Mailing List, MIPS Mailing List,
	linux-watchdog-u79uwXL29TY76Z2rM5mHXA, Rob Herring, Pawel Moll,
	Mark Rutland, Ian Campbell, Kumar Gala

On 25/11/15 20:14, Jonas Gorski wrote:
> On Tue, Nov 24, 2015 at 7:21 PM, Guenter Roeck <linux-0h96xk9xTtrk1uMJSBkQmQ@public.gmane.org> wrote:
>> On Sun, Nov 22, 2015 at 02:05:16PM +0000, Simon Arlott wrote:
>>> There is a level triggered interrupt for the watchdog timer as part of
>>> the bcm63xx_timer device. The interrupt occurs when the hardware watchdog
>>> timer reaches 50% of the remaining time.
>>>
>>> It is not possible to mask the interrupt within the bcm63xx_timer device.
>>> To get around this limitation, handle the interrupt by restarting the
>>> watchdog with the current remaining time (which will be half the previous
>>> timeout) so that the interrupt occurs again at 1/4th, 1/8th, etc. of the
>>> original timeout value until the watchdog forces a reboot.
>>>
>>> The software timer was restarting the hardware watchdog with a 85 second
>>> timeout until the software timer expired, and then causing a panic()
>>> about 42.5 seconds later when the hardware interrupt occurred. The
>>> hardware watchdog would not reboot until a further 42.5 seconds had
>>> passed.
>>>
>>> Remove the software timer and rely on the hardware timer directly,
>>> reducing the maximum timeout from 256 seconds to 85 seconds
>>> (2^32 / WDT_HZ).
>>>
>>
>> Florian,
>>
>> can you have a look into this patch and confirm that there is no better
>> way to clear the interrupt status ?
> 
> While the watchdog interrupt can't be masked, it should be able to be
> cleared by writing 1 to the appropriate bit in the timer block's
> interrupt status register. At least the broadcom sources do so.

Not according to the hardware itself:
[    6.674626] watchdog watchdog0: warning timer fired, reboot in 7499ms
[    6.681212] irq_bcm6345_l2_timer: bcm6345_timer_write_int_status: b0000083=08
[    6.688583] watchdog watchdog0: warning timer fired, reboot in 7486ms
[    6.695181] irq_bcm6345_l2_timer: bcm6345_timer_write_int_status: b0000083=08
[    6.702554] watchdog watchdog0: warning timer fired, reboot in 7472ms
[    6.709158] irq_bcm6345_l2_timer: bcm6345_timer_write_int_status: b0000083=08
[    6.716529] watchdog watchdog0: warning timer fired, reboot in 7458ms
[    6.723135] irq_bcm6345_l2_timer: bcm6345_timer_write_int_status: b0000083=08
[    6.730538] watchdog watchdog0: warning timer fired, reboot in 7444ms
[    6.737121] irq_bcm6345_l2_timer: bcm6345_timer_write_int_status: b0000083=08
[    6.744482] watchdog watchdog0: warning timer fired, reboot in 7430ms
[    6.751090] irq_bcm6345_l2_timer: bcm6345_timer_write_int_status: b0000083=08


typedef struct Timer {
    uint16        unused0;
    byte          TimerMask;
#define TIMER0EN        0x01
#define TIMER1EN        0x02
#define TIMER2EN        0x04
    byte          TimerInts;
#define TIMER0          0x01
#define TIMER1          0x02
#define TIMER2          0x04
#define WATCHDOG        0x08
...

-- 
Simon Arlott
--
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] 95+ messages in thread

* [PATCH (v2) 4/10] watchdog: bcm63xx_wdt: Handle hardware interrupt and remove software timer
@ 2015-11-25 22:33               ` Simon Arlott
  0 siblings, 0 replies; 95+ messages in thread
From: Simon Arlott @ 2015-11-25 22:33 UTC (permalink / raw)
  To: Guenter Roeck, devicetree, Ralf Baechle, Thomas Gleixner,
	Jason Cooper, Marc Zyngier, Kevin Cernekee, Florian Fainelli,
	Wim Van Sebroeck, Maxime Bizon, Linux Kernel Mailing List,
	linux-mips, linux-watchdog
  Cc: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
	Jonas Gorski

There is a level triggered interrupt for the watchdog timer as part of
the bcm63xx_timer device. The interrupt occurs when the hardware watchdog
timer reaches 50% of the remaining time.

It is not possible to mask the interrupt within the bcm63xx_timer device.
To get around this limitation, handle the interrupt by restarting the
watchdog with the current remaining time (which will be half the previous
timeout) so that the interrupt occurs again at 1/4th, 1/8th, etc. of the
original timeout value until the watchdog forces a reboot.

The software timer was restarting the hardware watchdog with a 85 second
timeout until the software timer expired, and then causing a panic()
about 42.5 seconds later when the hardware interrupt occurred. The
hardware watchdog would not reboot until a further 42.5 seconds had
passed.

Remove the software timer and rely on the hardware timer directly,
reducing the maximum timeout from 256 seconds to 85 seconds
(2^32 / WDT_HZ).

Signed-off-by: Simon Arlott <simon@fire.lp0.eu>
---
Initialise bcm63xx_wdt_device.running to false.

 drivers/watchdog/bcm63xx_wdt.c | 125 ++++++++++++++++++++++++-----------------
 1 file changed, 73 insertions(+), 52 deletions(-)

diff --git a/drivers/watchdog/bcm63xx_wdt.c b/drivers/watchdog/bcm63xx_wdt.c
index ab26fd9..3f55cba 100644
--- a/drivers/watchdog/bcm63xx_wdt.c
+++ b/drivers/watchdog/bcm63xx_wdt.c
@@ -3,6 +3,7 @@
  *
  *  Copyright (C) 2007, Miguel Gaio <miguel.gaio@efixo.com>
  *  Copyright (C) 2008, Florian Fainelli <florian@openwrt.org>
+ *  Copyright 2015 Simon Arlott
  *
  *  This program is free software; you can redistribute it and/or
  *  modify it under the terms of the GNU General Public License
@@ -20,11 +21,10 @@
 #include <linux/miscdevice.h>
 #include <linux/module.h>
 #include <linux/moduleparam.h>
+#include <linux/spinlock.h>
 #include <linux/types.h>
 #include <linux/uaccess.h>
 #include <linux/watchdog.h>
-#include <linux/timer.h>
-#include <linux/jiffies.h>
 #include <linux/interrupt.h>
 #include <linux/ptrace.h>
 #include <linux/resource.h>
@@ -37,16 +37,17 @@
 
 #define PFX KBUILD_MODNAME
 
-#define WDT_HZ		50000000 /* Fclk */
-#define WDT_DEFAULT_TIME	30      /* seconds */
-#define WDT_MAX_TIME		256     /* seconds */
+#define WDT_HZ			50000000		/* Fclk */
+#define WDT_DEFAULT_TIME	30			/* seconds */
+#define WDT_MAX_TIME		(0xffffffff / WDT_HZ)	/* seconds */
 
-static struct {
+struct bcm63xx_wdt_hw {
+	raw_spinlock_t lock;
 	void __iomem *regs;
-	struct timer_list timer;
 	unsigned long inuse;
-	atomic_t ticks;
-} bcm63xx_wdt_device;
+	bool running;
+};
+static struct bcm63xx_wdt_hw bcm63xx_wdt_device;
 
 static int expect_close;
 
@@ -59,48 +60,67 @@ MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
 /* HW functions */
 static void bcm63xx_wdt_hw_start(void)
 {
-	bcm_writel(0xfffffffe, bcm63xx_wdt_device.regs + WDT_DEFVAL_REG);
+	unsigned long flags;
+
+	raw_spin_lock_irqsave(&bcm63xx_wdt_device.lock, flags);
+	bcm_writel(wdt_time * WDT_HZ, bcm63xx_wdt_device.regs + WDT_DEFVAL_REG);
 	bcm_writel(WDT_START_1, bcm63xx_wdt_device.regs + WDT_CTL_REG);
 	bcm_writel(WDT_START_2, bcm63xx_wdt_device.regs + WDT_CTL_REG);
+	bcm63xx_wdt_device.running = true;
+	raw_spin_unlock_irqrestore(&bcm63xx_wdt_device.lock, flags);
 }
 
 static void bcm63xx_wdt_hw_stop(void)
 {
+	unsigned long flags;
+
+	raw_spin_lock_irqsave(&bcm63xx_wdt_device.lock, flags);
 	bcm_writel(WDT_STOP_1, bcm63xx_wdt_device.regs + WDT_CTL_REG);
 	bcm_writel(WDT_STOP_2, bcm63xx_wdt_device.regs + WDT_CTL_REG);
+	bcm63xx_wdt_device.running = false;
+	raw_spin_unlock_irqrestore(&bcm63xx_wdt_device.lock, flags);
 }
 
+/* The watchdog interrupt occurs when half the timeout is remaining */
 static void bcm63xx_wdt_isr(void *data)
 {
-	struct pt_regs *regs = get_irq_regs();
-
-	die(PFX " fire", regs);
-}
-
-static void bcm63xx_timer_tick(unsigned long unused)
-{
-	if (!atomic_dec_and_test(&bcm63xx_wdt_device.ticks)) {
-		bcm63xx_wdt_hw_start();
-		mod_timer(&bcm63xx_wdt_device.timer, jiffies + HZ);
-	} else
-		pr_crit("watchdog will restart system\n");
-}
-
-static void bcm63xx_wdt_pet(void)
-{
-	atomic_set(&bcm63xx_wdt_device.ticks, wdt_time);
-}
-
-static void bcm63xx_wdt_start(void)
-{
-	bcm63xx_wdt_pet();
-	bcm63xx_timer_tick(0);
-}
+	struct bcm63xx_wdt_hw *hw = &bcm63xx_wdt_device;
+	unsigned long flags;
+
+	raw_spin_lock_irqsave(&hw->lock, flags);
+	if (!hw->running) {
+		/* Stop the watchdog as it shouldn't be running */
+		bcm_writel(WDT_STOP_1, hw->regs + WDT_CTL_REG);
+		bcm_writel(WDT_STOP_2, hw->regs + WDT_CTL_REG);
+	} else {
+		u32 timeleft = bcm_readl(hw->regs + WDT_CTL_REG);
+		u32 ms;
+
+		if (timeleft >= 2) {
+			/* The only way to clear this level triggered interrupt
+			 * without disrupting the normal running of the watchdog
+			 * is to restart the watchdog with the current remaining
+			 * time value (which will be half the previous timeout)
+			 * so the interrupt occurs again at 1/4th, 1/8th, etc.
+			 * of the original timeout value until we reboot.
+			 *
+			 * This is done with a lock held in case userspace is
+			 * trying to restart the watchdog on another CPU.
+			 */
+			bcm_writel(timeleft, hw->regs + WDT_DEFVAL_REG);
+			bcm_writel(WDT_START_1, hw->regs + WDT_CTL_REG);
+			bcm_writel(WDT_START_2, hw->regs + WDT_CTL_REG);
+		} else {
+			/* The watchdog cannot be started with a time of less
+			 * than 2 ticks (it won't fire).
+			 */
+			die(PFX ": watchdog timer expired\n", get_irq_regs());
+		}
 
-static void bcm63xx_wdt_pause(void)
-{
-	del_timer_sync(&bcm63xx_wdt_device.timer);
-	bcm63xx_wdt_hw_stop();
+		ms = timeleft / (WDT_HZ / 1000);
+		pr_alert("warning timer fired, reboot in %ums\n", ms);
+	}
+	raw_spin_unlock_irqrestore(&hw->lock, flags);
 }
 
 static int bcm63xx_wdt_settimeout(int new_time)
@@ -118,17 +138,17 @@ static int bcm63xx_wdt_open(struct inode *inode, struct file *file)
 	if (test_and_set_bit(0, &bcm63xx_wdt_device.inuse))
 		return -EBUSY;
 
-	bcm63xx_wdt_start();
+	bcm63xx_wdt_hw_start();
 	return nonseekable_open(inode, file);
 }
 
 static int bcm63xx_wdt_release(struct inode *inode, struct file *file)
 {
 	if (expect_close == 42)
-		bcm63xx_wdt_pause();
+		bcm63xx_wdt_hw_stop();
 	else {
 		pr_crit("Unexpected close, not stopping watchdog!\n");
-		bcm63xx_wdt_start();
+		bcm63xx_wdt_hw_start();
 	}
 	clear_bit(0, &bcm63xx_wdt_device.inuse);
 	expect_close = 0;
@@ -153,7 +173,7 @@ static ssize_t bcm63xx_wdt_write(struct file *file, const char *data,
 					expect_close = 42;
 			}
 		}
-		bcm63xx_wdt_pet();
+		bcm63xx_wdt_hw_start();
 	}
 	return len;
 }
@@ -187,18 +207,18 @@ static long bcm63xx_wdt_ioctl(struct file *file, unsigned int cmd,
 			return -EFAULT;
 
 		if (new_value & WDIOS_DISABLECARD) {
-			bcm63xx_wdt_pause();
+			bcm63xx_wdt_hw_stop();
 			retval = 0;
 		}
 		if (new_value & WDIOS_ENABLECARD) {
-			bcm63xx_wdt_start();
+			bcm63xx_wdt_hw_start();
 			retval = 0;
 		}
 
 		return retval;
 
 	case WDIOC_KEEPALIVE:
-		bcm63xx_wdt_pet();
+		bcm63xx_wdt_hw_start();
 		return 0;
 
 	case WDIOC_SETTIMEOUT:
@@ -208,7 +228,7 @@ static long bcm63xx_wdt_ioctl(struct file *file, unsigned int cmd,
 		if (bcm63xx_wdt_settimeout(new_value))
 			return -EINVAL;
 
-		bcm63xx_wdt_pet();
+		bcm63xx_wdt_hw_start();
 
 	case WDIOC_GETTIMEOUT:
 		return put_user(wdt_time, p);
@@ -240,8 +260,6 @@ static int bcm63xx_wdt_probe(struct platform_device *pdev)
 	int ret;
 	struct resource *r;
 
-	setup_timer(&bcm63xx_wdt_device.timer, bcm63xx_timer_tick, 0L);
-
 	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 	if (!r) {
 		dev_err(&pdev->dev, "failed to get resources\n");
@@ -255,6 +273,9 @@ static int bcm63xx_wdt_probe(struct platform_device *pdev)
 		return -ENXIO;
 	}
 
+	raw_spin_lock_init(&bcm63xx_wdt_device.lock);
+	bcm63xx_wdt_device.running = false;
+
 	ret = bcm63xx_timer_register(TIMER_WDT_ID, bcm63xx_wdt_isr, NULL);
 	if (ret < 0) {
 		dev_err(&pdev->dev, "failed to register wdt timer isr\n");
@@ -264,8 +285,8 @@ static int bcm63xx_wdt_probe(struct platform_device *pdev)
 	if (bcm63xx_wdt_settimeout(wdt_time)) {
 		bcm63xx_wdt_settimeout(WDT_DEFAULT_TIME);
 		dev_info(&pdev->dev,
-			": wdt_time value must be 1 <= wdt_time <= 256, using %d\n",
-			wdt_time);
+			": wdt_time value must be 1 <= wdt_time <= %d, using %d\n",
+			WDT_MAX_TIME, wdt_time);
 	}
 
 	ret = misc_register(&bcm63xx_wdt_miscdev);
@@ -287,7 +308,7 @@ unregister_timer:
 static int bcm63xx_wdt_remove(struct platform_device *pdev)
 {
 	if (!nowayout)
-		bcm63xx_wdt_pause();
+		bcm63xx_wdt_hw_stop();
 
 	misc_deregister(&bcm63xx_wdt_miscdev);
 	bcm63xx_timer_unregister(TIMER_WDT_ID);
@@ -296,7 +317,7 @@ static int bcm63xx_wdt_remove(struct platform_device *pdev)
 
 static void bcm63xx_wdt_shutdown(struct platform_device *pdev)
 {
-	bcm63xx_wdt_pause();
+	bcm63xx_wdt_hw_stop();
 }
 
 static struct platform_driver bcm63xx_wdt_driver = {
-- 
2.1.4

-- 
Simon Arlott

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

* [PATCH (v2) 4/10] watchdog: bcm63xx_wdt: Handle hardware interrupt and remove software timer
@ 2015-11-25 22:33               ` Simon Arlott
  0 siblings, 0 replies; 95+ messages in thread
From: Simon Arlott @ 2015-11-25 22:33 UTC (permalink / raw)
  To: Guenter Roeck, devicetree-u79uwXL29TY76Z2rM5mHXA, Ralf Baechle,
	Thomas Gleixner, Jason Cooper, Marc Zyngier, Kevin Cernekee,
	Florian Fainelli, Wim Van Sebroeck, Maxime Bizon,
	Linux Kernel Mailing List, linux-mips-6z/3iImG2C8G8FEW9MqTrA,
	linux-watchdog-u79uwXL29TY76Z2rM5mHXA
  Cc: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
	Jonas Gorski

There is a level triggered interrupt for the watchdog timer as part of
the bcm63xx_timer device. The interrupt occurs when the hardware watchdog
timer reaches 50% of the remaining time.

It is not possible to mask the interrupt within the bcm63xx_timer device.
To get around this limitation, handle the interrupt by restarting the
watchdog with the current remaining time (which will be half the previous
timeout) so that the interrupt occurs again at 1/4th, 1/8th, etc. of the
original timeout value until the watchdog forces a reboot.

The software timer was restarting the hardware watchdog with a 85 second
timeout until the software timer expired, and then causing a panic()
about 42.5 seconds later when the hardware interrupt occurred. The
hardware watchdog would not reboot until a further 42.5 seconds had
passed.

Remove the software timer and rely on the hardware timer directly,
reducing the maximum timeout from 256 seconds to 85 seconds
(2^32 / WDT_HZ).

Signed-off-by: Simon Arlott <simon-A6De1vDTPLDsq35pWSNszA@public.gmane.org>
---
Initialise bcm63xx_wdt_device.running to false.

 drivers/watchdog/bcm63xx_wdt.c | 125 ++++++++++++++++++++++++-----------------
 1 file changed, 73 insertions(+), 52 deletions(-)

diff --git a/drivers/watchdog/bcm63xx_wdt.c b/drivers/watchdog/bcm63xx_wdt.c
index ab26fd9..3f55cba 100644
--- a/drivers/watchdog/bcm63xx_wdt.c
+++ b/drivers/watchdog/bcm63xx_wdt.c
@@ -3,6 +3,7 @@
  *
  *  Copyright (C) 2007, Miguel Gaio <miguel.gaio-HH44TBFINEIAvxtiuMwx3w@public.gmane.org>
  *  Copyright (C) 2008, Florian Fainelli <florian-p3rKhJxN3npAfugRpC6u6w@public.gmane.org>
+ *  Copyright 2015 Simon Arlott
  *
  *  This program is free software; you can redistribute it and/or
  *  modify it under the terms of the GNU General Public License
@@ -20,11 +21,10 @@
 #include <linux/miscdevice.h>
 #include <linux/module.h>
 #include <linux/moduleparam.h>
+#include <linux/spinlock.h>
 #include <linux/types.h>
 #include <linux/uaccess.h>
 #include <linux/watchdog.h>
-#include <linux/timer.h>
-#include <linux/jiffies.h>
 #include <linux/interrupt.h>
 #include <linux/ptrace.h>
 #include <linux/resource.h>
@@ -37,16 +37,17 @@
 
 #define PFX KBUILD_MODNAME
 
-#define WDT_HZ		50000000 /* Fclk */
-#define WDT_DEFAULT_TIME	30      /* seconds */
-#define WDT_MAX_TIME		256     /* seconds */
+#define WDT_HZ			50000000		/* Fclk */
+#define WDT_DEFAULT_TIME	30			/* seconds */
+#define WDT_MAX_TIME		(0xffffffff / WDT_HZ)	/* seconds */
 
-static struct {
+struct bcm63xx_wdt_hw {
+	raw_spinlock_t lock;
 	void __iomem *regs;
-	struct timer_list timer;
 	unsigned long inuse;
-	atomic_t ticks;
-} bcm63xx_wdt_device;
+	bool running;
+};
+static struct bcm63xx_wdt_hw bcm63xx_wdt_device;
 
 static int expect_close;
 
@@ -59,48 +60,67 @@ MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
 /* HW functions */
 static void bcm63xx_wdt_hw_start(void)
 {
-	bcm_writel(0xfffffffe, bcm63xx_wdt_device.regs + WDT_DEFVAL_REG);
+	unsigned long flags;
+
+	raw_spin_lock_irqsave(&bcm63xx_wdt_device.lock, flags);
+	bcm_writel(wdt_time * WDT_HZ, bcm63xx_wdt_device.regs + WDT_DEFVAL_REG);
 	bcm_writel(WDT_START_1, bcm63xx_wdt_device.regs + WDT_CTL_REG);
 	bcm_writel(WDT_START_2, bcm63xx_wdt_device.regs + WDT_CTL_REG);
+	bcm63xx_wdt_device.running = true;
+	raw_spin_unlock_irqrestore(&bcm63xx_wdt_device.lock, flags);
 }
 
 static void bcm63xx_wdt_hw_stop(void)
 {
+	unsigned long flags;
+
+	raw_spin_lock_irqsave(&bcm63xx_wdt_device.lock, flags);
 	bcm_writel(WDT_STOP_1, bcm63xx_wdt_device.regs + WDT_CTL_REG);
 	bcm_writel(WDT_STOP_2, bcm63xx_wdt_device.regs + WDT_CTL_REG);
+	bcm63xx_wdt_device.running = false;
+	raw_spin_unlock_irqrestore(&bcm63xx_wdt_device.lock, flags);
 }
 
+/* The watchdog interrupt occurs when half the timeout is remaining */
 static void bcm63xx_wdt_isr(void *data)
 {
-	struct pt_regs *regs = get_irq_regs();
-
-	die(PFX " fire", regs);
-}
-
-static void bcm63xx_timer_tick(unsigned long unused)
-{
-	if (!atomic_dec_and_test(&bcm63xx_wdt_device.ticks)) {
-		bcm63xx_wdt_hw_start();
-		mod_timer(&bcm63xx_wdt_device.timer, jiffies + HZ);
-	} else
-		pr_crit("watchdog will restart system\n");
-}
-
-static void bcm63xx_wdt_pet(void)
-{
-	atomic_set(&bcm63xx_wdt_device.ticks, wdt_time);
-}
-
-static void bcm63xx_wdt_start(void)
-{
-	bcm63xx_wdt_pet();
-	bcm63xx_timer_tick(0);
-}
+	struct bcm63xx_wdt_hw *hw = &bcm63xx_wdt_device;
+	unsigned long flags;
+
+	raw_spin_lock_irqsave(&hw->lock, flags);
+	if (!hw->running) {
+		/* Stop the watchdog as it shouldn't be running */
+		bcm_writel(WDT_STOP_1, hw->regs + WDT_CTL_REG);
+		bcm_writel(WDT_STOP_2, hw->regs + WDT_CTL_REG);
+	} else {
+		u32 timeleft = bcm_readl(hw->regs + WDT_CTL_REG);
+		u32 ms;
+
+		if (timeleft >= 2) {
+			/* The only way to clear this level triggered interrupt
+			 * without disrupting the normal running of the watchdog
+			 * is to restart the watchdog with the current remaining
+			 * time value (which will be half the previous timeout)
+			 * so the interrupt occurs again at 1/4th, 1/8th, etc.
+			 * of the original timeout value until we reboot.
+			 *
+			 * This is done with a lock held in case userspace is
+			 * trying to restart the watchdog on another CPU.
+			 */
+			bcm_writel(timeleft, hw->regs + WDT_DEFVAL_REG);
+			bcm_writel(WDT_START_1, hw->regs + WDT_CTL_REG);
+			bcm_writel(WDT_START_2, hw->regs + WDT_CTL_REG);
+		} else {
+			/* The watchdog cannot be started with a time of less
+			 * than 2 ticks (it won't fire).
+			 */
+			die(PFX ": watchdog timer expired\n", get_irq_regs());
+		}
 
-static void bcm63xx_wdt_pause(void)
-{
-	del_timer_sync(&bcm63xx_wdt_device.timer);
-	bcm63xx_wdt_hw_stop();
+		ms = timeleft / (WDT_HZ / 1000);
+		pr_alert("warning timer fired, reboot in %ums\n", ms);
+	}
+	raw_spin_unlock_irqrestore(&hw->lock, flags);
 }
 
 static int bcm63xx_wdt_settimeout(int new_time)
@@ -118,17 +138,17 @@ static int bcm63xx_wdt_open(struct inode *inode, struct file *file)
 	if (test_and_set_bit(0, &bcm63xx_wdt_device.inuse))
 		return -EBUSY;
 
-	bcm63xx_wdt_start();
+	bcm63xx_wdt_hw_start();
 	return nonseekable_open(inode, file);
 }
 
 static int bcm63xx_wdt_release(struct inode *inode, struct file *file)
 {
 	if (expect_close == 42)
-		bcm63xx_wdt_pause();
+		bcm63xx_wdt_hw_stop();
 	else {
 		pr_crit("Unexpected close, not stopping watchdog!\n");
-		bcm63xx_wdt_start();
+		bcm63xx_wdt_hw_start();
 	}
 	clear_bit(0, &bcm63xx_wdt_device.inuse);
 	expect_close = 0;
@@ -153,7 +173,7 @@ static ssize_t bcm63xx_wdt_write(struct file *file, const char *data,
 					expect_close = 42;
 			}
 		}
-		bcm63xx_wdt_pet();
+		bcm63xx_wdt_hw_start();
 	}
 	return len;
 }
@@ -187,18 +207,18 @@ static long bcm63xx_wdt_ioctl(struct file *file, unsigned int cmd,
 			return -EFAULT;
 
 		if (new_value & WDIOS_DISABLECARD) {
-			bcm63xx_wdt_pause();
+			bcm63xx_wdt_hw_stop();
 			retval = 0;
 		}
 		if (new_value & WDIOS_ENABLECARD) {
-			bcm63xx_wdt_start();
+			bcm63xx_wdt_hw_start();
 			retval = 0;
 		}
 
 		return retval;
 
 	case WDIOC_KEEPALIVE:
-		bcm63xx_wdt_pet();
+		bcm63xx_wdt_hw_start();
 		return 0;
 
 	case WDIOC_SETTIMEOUT:
@@ -208,7 +228,7 @@ static long bcm63xx_wdt_ioctl(struct file *file, unsigned int cmd,
 		if (bcm63xx_wdt_settimeout(new_value))
 			return -EINVAL;
 
-		bcm63xx_wdt_pet();
+		bcm63xx_wdt_hw_start();
 
 	case WDIOC_GETTIMEOUT:
 		return put_user(wdt_time, p);
@@ -240,8 +260,6 @@ static int bcm63xx_wdt_probe(struct platform_device *pdev)
 	int ret;
 	struct resource *r;
 
-	setup_timer(&bcm63xx_wdt_device.timer, bcm63xx_timer_tick, 0L);
-
 	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 	if (!r) {
 		dev_err(&pdev->dev, "failed to get resources\n");
@@ -255,6 +273,9 @@ static int bcm63xx_wdt_probe(struct platform_device *pdev)
 		return -ENXIO;
 	}
 
+	raw_spin_lock_init(&bcm63xx_wdt_device.lock);
+	bcm63xx_wdt_device.running = false;
+
 	ret = bcm63xx_timer_register(TIMER_WDT_ID, bcm63xx_wdt_isr, NULL);
 	if (ret < 0) {
 		dev_err(&pdev->dev, "failed to register wdt timer isr\n");
@@ -264,8 +285,8 @@ static int bcm63xx_wdt_probe(struct platform_device *pdev)
 	if (bcm63xx_wdt_settimeout(wdt_time)) {
 		bcm63xx_wdt_settimeout(WDT_DEFAULT_TIME);
 		dev_info(&pdev->dev,
-			": wdt_time value must be 1 <= wdt_time <= 256, using %d\n",
-			wdt_time);
+			": wdt_time value must be 1 <= wdt_time <= %d, using %d\n",
+			WDT_MAX_TIME, wdt_time);
 	}
 
 	ret = misc_register(&bcm63xx_wdt_miscdev);
@@ -287,7 +308,7 @@ unregister_timer:
 static int bcm63xx_wdt_remove(struct platform_device *pdev)
 {
 	if (!nowayout)
-		bcm63xx_wdt_pause();
+		bcm63xx_wdt_hw_stop();
 
 	misc_deregister(&bcm63xx_wdt_miscdev);
 	bcm63xx_timer_unregister(TIMER_WDT_ID);
@@ -296,7 +317,7 @@ static int bcm63xx_wdt_remove(struct platform_device *pdev)
 
 static void bcm63xx_wdt_shutdown(struct platform_device *pdev)
 {
-	bcm63xx_wdt_pause();
+	bcm63xx_wdt_hw_stop();
 }
 
 static struct platform_driver bcm63xx_wdt_driver = {
-- 
2.1.4

-- 
Simon Arlott
--
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] 95+ messages in thread

* [PATCH (v3) 5/11] watchdog: bcm63xx_wdt: Use WATCHDOG_CORE
@ 2015-11-25 22:40                 ` Simon Arlott
  0 siblings, 0 replies; 95+ messages in thread
From: Simon Arlott @ 2015-11-25 22:40 UTC (permalink / raw)
  To: Guenter Roeck, devicetree, Ralf Baechle, Thomas Gleixner,
	Jason Cooper, Marc Zyngier, Kevin Cernekee, Florian Fainelli,
	Wim Van Sebroeck, Maxime Bizon, Linux Kernel Mailing List,
	linux-mips, linux-watchdog
  Cc: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
	Jonas Gorski

Convert bcm63xx_wdt to use WATCHDOG_CORE.

The default and maximum time constants that are only used once have been
moved to the initialisation of the struct watchdog_device.

Signed-off-by: Simon Arlott <simon@fire.lp0.eu>
---
Patch 7 split into two patches.

On 25/11/15 02:44, Guenter Roeck wrote:
> If I see correctly, there is no ping function. In that case, the watchdog core
> will call the start function after updating the timeout, so there is no need
> to do it here.

Fixed.
 
>> +static const struct watchdog_info bcm63xx_wdt_info = {
>> +	.options = WDIOC_GETTIMELEFT | WDIOF_SETTIMEOUT |
> 
> Where is the gettimeleft function ? I think you are adding it with a later patch,
> but then you should set the flag there, not here.

Removed WDIOC_GETTIMELEFT completely because it's not a flag.

>> +	hw = devm_kzalloc(&pdev->dev, sizeof(*hw), GFP_KERNEL);
>> +	wdd = devm_kzalloc(&pdev->dev, sizeof(*wdd), GFP_KERNEL);
> 
> It would be better to allocate wdd as part of struct bcm63xx_wdt_hw.

Fixed.

>> -	misc_deregister(&bcm63xx_wdt_miscdev);
>>   	bcm63xx_timer_unregister(TIMER_WDT_ID);
>> +	watchdog_unregister_device(wdd);
> 
> Shouldn't that come first, before unregistering the timer ?

No, because wdd->dev is used in the interrupt handler. The handler will
not be called after bcm63xx_timer_unregister() is called.

Moved registration of the timer in the probe function to after register
of the watchdog device because the interrupt handler uses wdd->dev.

 drivers/watchdog/Kconfig       |   1 +
 drivers/watchdog/bcm63xx_wdt.c | 259 +++++++++++++----------------------------
 2 files changed, 79 insertions(+), 181 deletions(-)

diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index 7a8a6c6..6815b74 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -1273,6 +1273,7 @@ config OCTEON_WDT
 config BCM63XX_WDT
 	tristate "Broadcom BCM63xx hardware watchdog"
 	depends on BCM63XX
+	select WATCHDOG_CORE
 	help
 	  Watchdog driver for the built in watchdog hardware in Broadcom
 	  BCM63xx SoC.
diff --git a/drivers/watchdog/bcm63xx_wdt.c b/drivers/watchdog/bcm63xx_wdt.c
index 3f55cba..2257924 100644
--- a/drivers/watchdog/bcm63xx_wdt.c
+++ b/drivers/watchdog/bcm63xx_wdt.c
@@ -13,20 +13,15 @@
 
 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
 
-#include <linux/bitops.h>
 #include <linux/errno.h>
-#include <linux/fs.h>
 #include <linux/io.h>
 #include <linux/kernel.h>
-#include <linux/miscdevice.h>
 #include <linux/module.h>
 #include <linux/moduleparam.h>
 #include <linux/spinlock.h>
 #include <linux/types.h>
-#include <linux/uaccess.h>
 #include <linux/watchdog.h>
 #include <linux/interrupt.h>
-#include <linux/ptrace.h>
 #include <linux/resource.h>
 #include <linux/platform_device.h>
 
@@ -38,53 +33,59 @@
 #define PFX KBUILD_MODNAME
 
 #define WDT_HZ			50000000		/* Fclk */
-#define WDT_DEFAULT_TIME	30			/* seconds */
-#define WDT_MAX_TIME		(0xffffffff / WDT_HZ)	/* seconds */
 
 struct bcm63xx_wdt_hw {
+	struct watchdog_device wdd;
 	raw_spinlock_t lock;
 	void __iomem *regs;
-	unsigned long inuse;
 	bool running;
 };
-static struct bcm63xx_wdt_hw bcm63xx_wdt_device;
 
-static int expect_close;
+#define to_wdt_hw(x) container_of(x, struct bcm63xx_wdt_hw, wdd)
 
-static int wdt_time = WDT_DEFAULT_TIME;
 static bool nowayout = WATCHDOG_NOWAYOUT;
 module_param(nowayout, bool, 0);
 MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
 	__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
 
-/* HW functions */
-static void bcm63xx_wdt_hw_start(void)
+static int bcm63xx_wdt_start(struct watchdog_device *wdd)
 {
+	struct bcm63xx_wdt_hw *hw = to_wdt_hw(wdd);
 	unsigned long flags;
 
-	raw_spin_lock_irqsave(&bcm63xx_wdt_device.lock, flags);
-	bcm_writel(wdt_time * WDT_HZ, bcm63xx_wdt_device.regs + WDT_DEFVAL_REG);
-	bcm_writel(WDT_START_1, bcm63xx_wdt_device.regs + WDT_CTL_REG);
-	bcm_writel(WDT_START_2, bcm63xx_wdt_device.regs + WDT_CTL_REG);
-	bcm63xx_wdt_device.running = true;
-	raw_spin_unlock_irqrestore(&bcm63xx_wdt_device.lock, flags);
+	raw_spin_lock_irqsave(&hw->lock, flags);
+	bcm_writel(wdd->timeout * WDT_HZ, hw->regs + WDT_DEFVAL_REG);
+	bcm_writel(WDT_START_1, hw->regs + WDT_CTL_REG);
+	bcm_writel(WDT_START_2, hw->regs + WDT_CTL_REG);
+	hw->running = true;
+	raw_spin_unlock_irqrestore(&hw->lock, flags);
+	return 0;
 }
 
-static void bcm63xx_wdt_hw_stop(void)
+static int bcm63xx_wdt_stop(struct watchdog_device *wdd)
 {
+	struct bcm63xx_wdt_hw *hw = to_wdt_hw(wdd);
 	unsigned long flags;
 
-	raw_spin_lock_irqsave(&bcm63xx_wdt_device.lock, flags);
-	bcm_writel(WDT_STOP_1, bcm63xx_wdt_device.regs + WDT_CTL_REG);
-	bcm_writel(WDT_STOP_2, bcm63xx_wdt_device.regs + WDT_CTL_REG);
-	bcm63xx_wdt_device.running = false;
-	raw_spin_unlock_irqrestore(&bcm63xx_wdt_device.lock, flags);
+	raw_spin_lock_irqsave(&hw->lock, flags);
+	bcm_writel(WDT_STOP_1, hw->regs + WDT_CTL_REG);
+	bcm_writel(WDT_STOP_2, hw->regs + WDT_CTL_REG);
+	hw->running = false;
+	raw_spin_unlock_irqrestore(&hw->lock, flags);
+	return 0;
+}
+
+static int bcm63xx_wdt_set_timeout(struct watchdog_device *wdd,
+	unsigned int timeout)
+{
+	wdd->timeout = timeout;
+	return 0;
 }
 
 /* The watchdog interrupt occurs when half the timeout is remaining */
 static void bcm63xx_wdt_isr(void *data)
 {
-	struct bcm63xx_wdt_hw *hw = &bcm63xx_wdt_device;
+	struct bcm63xx_wdt_hw *hw = data;
 	unsigned long flags;
 
 	raw_spin_lock_irqsave(&hw->lock, flags);
@@ -118,147 +119,36 @@ static void bcm63xx_wdt_isr(void *data)
 		}
 
 		ms = timeleft / (WDT_HZ / 1000);
-		pr_alert("warning timer fired, reboot in %ums\n", ms);
+		dev_alert(hw->wdd.dev,
+			"warning timer fired, reboot in %ums\n", ms);
 	}
 	raw_spin_unlock_irqrestore(&hw->lock, flags);
 }
 
-static int bcm63xx_wdt_settimeout(int new_time)
-{
-	if ((new_time <= 0) || (new_time > WDT_MAX_TIME))
-		return -EINVAL;
-
-	wdt_time = new_time;
-
-	return 0;
-}
-
-static int bcm63xx_wdt_open(struct inode *inode, struct file *file)
-{
-	if (test_and_set_bit(0, &bcm63xx_wdt_device.inuse))
-		return -EBUSY;
-
-	bcm63xx_wdt_hw_start();
-	return nonseekable_open(inode, file);
-}
-
-static int bcm63xx_wdt_release(struct inode *inode, struct file *file)
-{
-	if (expect_close == 42)
-		bcm63xx_wdt_hw_stop();
-	else {
-		pr_crit("Unexpected close, not stopping watchdog!\n");
-		bcm63xx_wdt_hw_start();
-	}
-	clear_bit(0, &bcm63xx_wdt_device.inuse);
-	expect_close = 0;
-	return 0;
-}
-
-static ssize_t bcm63xx_wdt_write(struct file *file, const char *data,
-				size_t len, loff_t *ppos)
-{
-	if (len) {
-		if (!nowayout) {
-			size_t i;
-
-			/* In case it was set long ago */
-			expect_close = 0;
-
-			for (i = 0; i != len; i++) {
-				char c;
-				if (get_user(c, data + i))
-					return -EFAULT;
-				if (c == 'V')
-					expect_close = 42;
-			}
-		}
-		bcm63xx_wdt_hw_start();
-	}
-	return len;
-}
-
-static struct watchdog_info bcm63xx_wdt_info = {
-	.identity       = PFX,
-	.options        = WDIOF_SETTIMEOUT |
-				WDIOF_KEEPALIVEPING |
-				WDIOF_MAGICCLOSE,
+static struct watchdog_ops bcm63xx_wdt_ops = {
+	.owner = THIS_MODULE,
+	.start = bcm63xx_wdt_start,
+	.stop = bcm63xx_wdt_stop,
+	.set_timeout = bcm63xx_wdt_set_timeout,
 };
 
-
-static long bcm63xx_wdt_ioctl(struct file *file, unsigned int cmd,
-				unsigned long arg)
-{
-	void __user *argp = (void __user *)arg;
-	int __user *p = argp;
-	int new_value, retval = -EINVAL;
-
-	switch (cmd) {
-	case WDIOC_GETSUPPORT:
-		return copy_to_user(argp, &bcm63xx_wdt_info,
-			sizeof(bcm63xx_wdt_info)) ? -EFAULT : 0;
-
-	case WDIOC_GETSTATUS:
-	case WDIOC_GETBOOTSTATUS:
-		return put_user(0, p);
-
-	case WDIOC_SETOPTIONS:
-		if (get_user(new_value, p))
-			return -EFAULT;
-
-		if (new_value & WDIOS_DISABLECARD) {
-			bcm63xx_wdt_hw_stop();
-			retval = 0;
-		}
-		if (new_value & WDIOS_ENABLECARD) {
-			bcm63xx_wdt_hw_start();
-			retval = 0;
-		}
-
-		return retval;
-
-	case WDIOC_KEEPALIVE:
-		bcm63xx_wdt_hw_start();
-		return 0;
-
-	case WDIOC_SETTIMEOUT:
-		if (get_user(new_value, p))
-			return -EFAULT;
-
-		if (bcm63xx_wdt_settimeout(new_value))
-			return -EINVAL;
-
-		bcm63xx_wdt_hw_start();
-
-	case WDIOC_GETTIMEOUT:
-		return put_user(wdt_time, p);
-
-	default:
-		return -ENOTTY;
-
-	}
-}
-
-static const struct file_operations bcm63xx_wdt_fops = {
-	.owner		= THIS_MODULE,
-	.llseek		= no_llseek,
-	.write		= bcm63xx_wdt_write,
-	.unlocked_ioctl	= bcm63xx_wdt_ioctl,
-	.open		= bcm63xx_wdt_open,
-	.release	= bcm63xx_wdt_release,
-};
-
-static struct miscdevice bcm63xx_wdt_miscdev = {
-	.minor	= WATCHDOG_MINOR,
-	.name	= "watchdog",
-	.fops	= &bcm63xx_wdt_fops,
+static const struct watchdog_info bcm63xx_wdt_info = {
+	.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
+	.identity = "BCM63xx Watchdog",
 };
 
-
 static int bcm63xx_wdt_probe(struct platform_device *pdev)
 {
-	int ret;
+	struct bcm63xx_wdt_hw *hw;
+	struct watchdog_device *wdd;
 	struct resource *r;
+	int ret;
+
+	hw = devm_kzalloc(&pdev->dev, sizeof(*hw), GFP_KERNEL);
+	if (!hw)
+		return -ENOMEM;
+
+	wdd = &hw->wdd;
 
 	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 	if (!r) {
@@ -266,58 +156,65 @@ static int bcm63xx_wdt_probe(struct platform_device *pdev)
 		return -ENODEV;
 	}
 
-	bcm63xx_wdt_device.regs = devm_ioremap_nocache(&pdev->dev, r->start,
-							resource_size(r));
-	if (!bcm63xx_wdt_device.regs) {
+	hw->regs = devm_ioremap_nocache(&pdev->dev, r->start, resource_size(r));
+	if (!hw->regs) {
 		dev_err(&pdev->dev, "failed to remap I/O resources\n");
 		return -ENXIO;
 	}
 
-	raw_spin_lock_init(&bcm63xx_wdt_device.lock);
-	bcm63xx_wdt_device.running = false;
+	raw_spin_lock_init(&hw->lock);
+	hw->running = false;
 
-	ret = bcm63xx_timer_register(TIMER_WDT_ID, bcm63xx_wdt_isr, NULL);
+	wdd->parent = &pdev->dev;
+	wdd->ops = &bcm63xx_wdt_ops;
+	wdd->info = &bcm63xx_wdt_info;
+	wdd->min_timeout = 1;
+	wdd->max_timeout = 0xffffffff / WDT_HZ;
+	wdd->timeout = min(30U, wdd->max_timeout);
+
+	platform_set_drvdata(pdev, hw);
+
+	watchdog_init_timeout(wdd, 0, &pdev->dev);
+	watchdog_set_nowayout(wdd, nowayout);
+
+	ret = watchdog_register_device(wdd);
 	if (ret < 0) {
-		dev_err(&pdev->dev, "failed to register wdt timer isr\n");
+		dev_err(&pdev->dev, "failed to register watchdog device\n");
 		return ret;
 	}
 
-	if (bcm63xx_wdt_settimeout(wdt_time)) {
-		bcm63xx_wdt_settimeout(WDT_DEFAULT_TIME);
-		dev_info(&pdev->dev,
-			": wdt_time value must be 1 <= wdt_time <= %d, using %d\n",
-			WDT_MAX_TIME, wdt_time);
-	}
-
-	ret = misc_register(&bcm63xx_wdt_miscdev);
+	ret = bcm63xx_timer_register(TIMER_WDT_ID, bcm63xx_wdt_isr, hw);
 	if (ret < 0) {
-		dev_err(&pdev->dev, "failed to register watchdog device\n");
-		goto unregister_timer;
+		dev_err(&pdev->dev, "failed to register wdt timer isr\n");
+		goto unregister_watchdog;
 	}
 
-	dev_info(&pdev->dev, " started, timer margin: %d sec\n",
-						WDT_DEFAULT_TIME);
+	dev_info(&pdev->dev,
+		"%s at MMIO 0x%p (timeout = %us, max_timeout = %us)",
+		dev_name(wdd->dev), hw->regs,
+		wdd->timeout, wdd->max_timeout);
 
 	return 0;
 
-unregister_timer:
-	bcm63xx_timer_unregister(TIMER_WDT_ID);
+unregister_watchdog:
+	watchdog_unregister_device(wdd);
 	return ret;
 }
 
 static int bcm63xx_wdt_remove(struct platform_device *pdev)
 {
-	if (!nowayout)
-		bcm63xx_wdt_hw_stop();
+	struct bcm63xx_wdt_hw *hw = platform_get_drvdata(pdev);
 
-	misc_deregister(&bcm63xx_wdt_miscdev);
 	bcm63xx_timer_unregister(TIMER_WDT_ID);
+	watchdog_unregister_device(&hw->wdd);
 	return 0;
 }
 
 static void bcm63xx_wdt_shutdown(struct platform_device *pdev)
 {
-	bcm63xx_wdt_hw_stop();
+	struct bcm63xx_wdt_hw *hw = platform_get_drvdata(pdev);
+
+	bcm63xx_wdt_stop(&hw->wdd);
 }
 
 static struct platform_driver bcm63xx_wdt_driver = {
-- 
2.1.4

-- 
Simon Arlott

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

* [PATCH (v3) 5/11] watchdog: bcm63xx_wdt: Use WATCHDOG_CORE
@ 2015-11-25 22:40                 ` Simon Arlott
  0 siblings, 0 replies; 95+ messages in thread
From: Simon Arlott @ 2015-11-25 22:40 UTC (permalink / raw)
  To: Guenter Roeck, devicetree-u79uwXL29TY76Z2rM5mHXA, Ralf Baechle,
	Thomas Gleixner, Jason Cooper, Marc Zyngier, Kevin Cernekee,
	Florian Fainelli, Wim Van Sebroeck, Maxime Bizon,
	Linux Kernel Mailing List, linux-mips-6z/3iImG2C8G8FEW9MqTrA,
	linux-watchdog-u79uwXL29TY76Z2rM5mHXA
  Cc: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
	Jonas Gorski

Convert bcm63xx_wdt to use WATCHDOG_CORE.

The default and maximum time constants that are only used once have been
moved to the initialisation of the struct watchdog_device.

Signed-off-by: Simon Arlott <simon-A6De1vDTPLDsq35pWSNszA@public.gmane.org>
---
Patch 7 split into two patches.

On 25/11/15 02:44, Guenter Roeck wrote:
> If I see correctly, there is no ping function. In that case, the watchdog core
> will call the start function after updating the timeout, so there is no need
> to do it here.

Fixed.
 
>> +static const struct watchdog_info bcm63xx_wdt_info = {
>> +	.options = WDIOC_GETTIMELEFT | WDIOF_SETTIMEOUT |
> 
> Where is the gettimeleft function ? I think you are adding it with a later patch,
> but then you should set the flag there, not here.

Removed WDIOC_GETTIMELEFT completely because it's not a flag.

>> +	hw = devm_kzalloc(&pdev->dev, sizeof(*hw), GFP_KERNEL);
>> +	wdd = devm_kzalloc(&pdev->dev, sizeof(*wdd), GFP_KERNEL);
> 
> It would be better to allocate wdd as part of struct bcm63xx_wdt_hw.

Fixed.

>> -	misc_deregister(&bcm63xx_wdt_miscdev);
>>   	bcm63xx_timer_unregister(TIMER_WDT_ID);
>> +	watchdog_unregister_device(wdd);
> 
> Shouldn't that come first, before unregistering the timer ?

No, because wdd->dev is used in the interrupt handler. The handler will
not be called after bcm63xx_timer_unregister() is called.

Moved registration of the timer in the probe function to after register
of the watchdog device because the interrupt handler uses wdd->dev.

 drivers/watchdog/Kconfig       |   1 +
 drivers/watchdog/bcm63xx_wdt.c | 259 +++++++++++++----------------------------
 2 files changed, 79 insertions(+), 181 deletions(-)

diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index 7a8a6c6..6815b74 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -1273,6 +1273,7 @@ config OCTEON_WDT
 config BCM63XX_WDT
 	tristate "Broadcom BCM63xx hardware watchdog"
 	depends on BCM63XX
+	select WATCHDOG_CORE
 	help
 	  Watchdog driver for the built in watchdog hardware in Broadcom
 	  BCM63xx SoC.
diff --git a/drivers/watchdog/bcm63xx_wdt.c b/drivers/watchdog/bcm63xx_wdt.c
index 3f55cba..2257924 100644
--- a/drivers/watchdog/bcm63xx_wdt.c
+++ b/drivers/watchdog/bcm63xx_wdt.c
@@ -13,20 +13,15 @@
 
 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
 
-#include <linux/bitops.h>
 #include <linux/errno.h>
-#include <linux/fs.h>
 #include <linux/io.h>
 #include <linux/kernel.h>
-#include <linux/miscdevice.h>
 #include <linux/module.h>
 #include <linux/moduleparam.h>
 #include <linux/spinlock.h>
 #include <linux/types.h>
-#include <linux/uaccess.h>
 #include <linux/watchdog.h>
 #include <linux/interrupt.h>
-#include <linux/ptrace.h>
 #include <linux/resource.h>
 #include <linux/platform_device.h>
 
@@ -38,53 +33,59 @@
 #define PFX KBUILD_MODNAME
 
 #define WDT_HZ			50000000		/* Fclk */
-#define WDT_DEFAULT_TIME	30			/* seconds */
-#define WDT_MAX_TIME		(0xffffffff / WDT_HZ)	/* seconds */
 
 struct bcm63xx_wdt_hw {
+	struct watchdog_device wdd;
 	raw_spinlock_t lock;
 	void __iomem *regs;
-	unsigned long inuse;
 	bool running;
 };
-static struct bcm63xx_wdt_hw bcm63xx_wdt_device;
 
-static int expect_close;
+#define to_wdt_hw(x) container_of(x, struct bcm63xx_wdt_hw, wdd)
 
-static int wdt_time = WDT_DEFAULT_TIME;
 static bool nowayout = WATCHDOG_NOWAYOUT;
 module_param(nowayout, bool, 0);
 MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
 	__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
 
-/* HW functions */
-static void bcm63xx_wdt_hw_start(void)
+static int bcm63xx_wdt_start(struct watchdog_device *wdd)
 {
+	struct bcm63xx_wdt_hw *hw = to_wdt_hw(wdd);
 	unsigned long flags;
 
-	raw_spin_lock_irqsave(&bcm63xx_wdt_device.lock, flags);
-	bcm_writel(wdt_time * WDT_HZ, bcm63xx_wdt_device.regs + WDT_DEFVAL_REG);
-	bcm_writel(WDT_START_1, bcm63xx_wdt_device.regs + WDT_CTL_REG);
-	bcm_writel(WDT_START_2, bcm63xx_wdt_device.regs + WDT_CTL_REG);
-	bcm63xx_wdt_device.running = true;
-	raw_spin_unlock_irqrestore(&bcm63xx_wdt_device.lock, flags);
+	raw_spin_lock_irqsave(&hw->lock, flags);
+	bcm_writel(wdd->timeout * WDT_HZ, hw->regs + WDT_DEFVAL_REG);
+	bcm_writel(WDT_START_1, hw->regs + WDT_CTL_REG);
+	bcm_writel(WDT_START_2, hw->regs + WDT_CTL_REG);
+	hw->running = true;
+	raw_spin_unlock_irqrestore(&hw->lock, flags);
+	return 0;
 }
 
-static void bcm63xx_wdt_hw_stop(void)
+static int bcm63xx_wdt_stop(struct watchdog_device *wdd)
 {
+	struct bcm63xx_wdt_hw *hw = to_wdt_hw(wdd);
 	unsigned long flags;
 
-	raw_spin_lock_irqsave(&bcm63xx_wdt_device.lock, flags);
-	bcm_writel(WDT_STOP_1, bcm63xx_wdt_device.regs + WDT_CTL_REG);
-	bcm_writel(WDT_STOP_2, bcm63xx_wdt_device.regs + WDT_CTL_REG);
-	bcm63xx_wdt_device.running = false;
-	raw_spin_unlock_irqrestore(&bcm63xx_wdt_device.lock, flags);
+	raw_spin_lock_irqsave(&hw->lock, flags);
+	bcm_writel(WDT_STOP_1, hw->regs + WDT_CTL_REG);
+	bcm_writel(WDT_STOP_2, hw->regs + WDT_CTL_REG);
+	hw->running = false;
+	raw_spin_unlock_irqrestore(&hw->lock, flags);
+	return 0;
+}
+
+static int bcm63xx_wdt_set_timeout(struct watchdog_device *wdd,
+	unsigned int timeout)
+{
+	wdd->timeout = timeout;
+	return 0;
 }
 
 /* The watchdog interrupt occurs when half the timeout is remaining */
 static void bcm63xx_wdt_isr(void *data)
 {
-	struct bcm63xx_wdt_hw *hw = &bcm63xx_wdt_device;
+	struct bcm63xx_wdt_hw *hw = data;
 	unsigned long flags;
 
 	raw_spin_lock_irqsave(&hw->lock, flags);
@@ -118,147 +119,36 @@ static void bcm63xx_wdt_isr(void *data)
 		}
 
 		ms = timeleft / (WDT_HZ / 1000);
-		pr_alert("warning timer fired, reboot in %ums\n", ms);
+		dev_alert(hw->wdd.dev,
+			"warning timer fired, reboot in %ums\n", ms);
 	}
 	raw_spin_unlock_irqrestore(&hw->lock, flags);
 }
 
-static int bcm63xx_wdt_settimeout(int new_time)
-{
-	if ((new_time <= 0) || (new_time > WDT_MAX_TIME))
-		return -EINVAL;
-
-	wdt_time = new_time;
-
-	return 0;
-}
-
-static int bcm63xx_wdt_open(struct inode *inode, struct file *file)
-{
-	if (test_and_set_bit(0, &bcm63xx_wdt_device.inuse))
-		return -EBUSY;
-
-	bcm63xx_wdt_hw_start();
-	return nonseekable_open(inode, file);
-}
-
-static int bcm63xx_wdt_release(struct inode *inode, struct file *file)
-{
-	if (expect_close == 42)
-		bcm63xx_wdt_hw_stop();
-	else {
-		pr_crit("Unexpected close, not stopping watchdog!\n");
-		bcm63xx_wdt_hw_start();
-	}
-	clear_bit(0, &bcm63xx_wdt_device.inuse);
-	expect_close = 0;
-	return 0;
-}
-
-static ssize_t bcm63xx_wdt_write(struct file *file, const char *data,
-				size_t len, loff_t *ppos)
-{
-	if (len) {
-		if (!nowayout) {
-			size_t i;
-
-			/* In case it was set long ago */
-			expect_close = 0;
-
-			for (i = 0; i != len; i++) {
-				char c;
-				if (get_user(c, data + i))
-					return -EFAULT;
-				if (c == 'V')
-					expect_close = 42;
-			}
-		}
-		bcm63xx_wdt_hw_start();
-	}
-	return len;
-}
-
-static struct watchdog_info bcm63xx_wdt_info = {
-	.identity       = PFX,
-	.options        = WDIOF_SETTIMEOUT |
-				WDIOF_KEEPALIVEPING |
-				WDIOF_MAGICCLOSE,
+static struct watchdog_ops bcm63xx_wdt_ops = {
+	.owner = THIS_MODULE,
+	.start = bcm63xx_wdt_start,
+	.stop = bcm63xx_wdt_stop,
+	.set_timeout = bcm63xx_wdt_set_timeout,
 };
 
-
-static long bcm63xx_wdt_ioctl(struct file *file, unsigned int cmd,
-				unsigned long arg)
-{
-	void __user *argp = (void __user *)arg;
-	int __user *p = argp;
-	int new_value, retval = -EINVAL;
-
-	switch (cmd) {
-	case WDIOC_GETSUPPORT:
-		return copy_to_user(argp, &bcm63xx_wdt_info,
-			sizeof(bcm63xx_wdt_info)) ? -EFAULT : 0;
-
-	case WDIOC_GETSTATUS:
-	case WDIOC_GETBOOTSTATUS:
-		return put_user(0, p);
-
-	case WDIOC_SETOPTIONS:
-		if (get_user(new_value, p))
-			return -EFAULT;
-
-		if (new_value & WDIOS_DISABLECARD) {
-			bcm63xx_wdt_hw_stop();
-			retval = 0;
-		}
-		if (new_value & WDIOS_ENABLECARD) {
-			bcm63xx_wdt_hw_start();
-			retval = 0;
-		}
-
-		return retval;
-
-	case WDIOC_KEEPALIVE:
-		bcm63xx_wdt_hw_start();
-		return 0;
-
-	case WDIOC_SETTIMEOUT:
-		if (get_user(new_value, p))
-			return -EFAULT;
-
-		if (bcm63xx_wdt_settimeout(new_value))
-			return -EINVAL;
-
-		bcm63xx_wdt_hw_start();
-
-	case WDIOC_GETTIMEOUT:
-		return put_user(wdt_time, p);
-
-	default:
-		return -ENOTTY;
-
-	}
-}
-
-static const struct file_operations bcm63xx_wdt_fops = {
-	.owner		= THIS_MODULE,
-	.llseek		= no_llseek,
-	.write		= bcm63xx_wdt_write,
-	.unlocked_ioctl	= bcm63xx_wdt_ioctl,
-	.open		= bcm63xx_wdt_open,
-	.release	= bcm63xx_wdt_release,
-};
-
-static struct miscdevice bcm63xx_wdt_miscdev = {
-	.minor	= WATCHDOG_MINOR,
-	.name	= "watchdog",
-	.fops	= &bcm63xx_wdt_fops,
+static const struct watchdog_info bcm63xx_wdt_info = {
+	.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
+	.identity = "BCM63xx Watchdog",
 };
 
-
 static int bcm63xx_wdt_probe(struct platform_device *pdev)
 {
-	int ret;
+	struct bcm63xx_wdt_hw *hw;
+	struct watchdog_device *wdd;
 	struct resource *r;
+	int ret;
+
+	hw = devm_kzalloc(&pdev->dev, sizeof(*hw), GFP_KERNEL);
+	if (!hw)
+		return -ENOMEM;
+
+	wdd = &hw->wdd;
 
 	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 	if (!r) {
@@ -266,58 +156,65 @@ static int bcm63xx_wdt_probe(struct platform_device *pdev)
 		return -ENODEV;
 	}
 
-	bcm63xx_wdt_device.regs = devm_ioremap_nocache(&pdev->dev, r->start,
-							resource_size(r));
-	if (!bcm63xx_wdt_device.regs) {
+	hw->regs = devm_ioremap_nocache(&pdev->dev, r->start, resource_size(r));
+	if (!hw->regs) {
 		dev_err(&pdev->dev, "failed to remap I/O resources\n");
 		return -ENXIO;
 	}
 
-	raw_spin_lock_init(&bcm63xx_wdt_device.lock);
-	bcm63xx_wdt_device.running = false;
+	raw_spin_lock_init(&hw->lock);
+	hw->running = false;
 
-	ret = bcm63xx_timer_register(TIMER_WDT_ID, bcm63xx_wdt_isr, NULL);
+	wdd->parent = &pdev->dev;
+	wdd->ops = &bcm63xx_wdt_ops;
+	wdd->info = &bcm63xx_wdt_info;
+	wdd->min_timeout = 1;
+	wdd->max_timeout = 0xffffffff / WDT_HZ;
+	wdd->timeout = min(30U, wdd->max_timeout);
+
+	platform_set_drvdata(pdev, hw);
+
+	watchdog_init_timeout(wdd, 0, &pdev->dev);
+	watchdog_set_nowayout(wdd, nowayout);
+
+	ret = watchdog_register_device(wdd);
 	if (ret < 0) {
-		dev_err(&pdev->dev, "failed to register wdt timer isr\n");
+		dev_err(&pdev->dev, "failed to register watchdog device\n");
 		return ret;
 	}
 
-	if (bcm63xx_wdt_settimeout(wdt_time)) {
-		bcm63xx_wdt_settimeout(WDT_DEFAULT_TIME);
-		dev_info(&pdev->dev,
-			": wdt_time value must be 1 <= wdt_time <= %d, using %d\n",
-			WDT_MAX_TIME, wdt_time);
-	}
-
-	ret = misc_register(&bcm63xx_wdt_miscdev);
+	ret = bcm63xx_timer_register(TIMER_WDT_ID, bcm63xx_wdt_isr, hw);
 	if (ret < 0) {
-		dev_err(&pdev->dev, "failed to register watchdog device\n");
-		goto unregister_timer;
+		dev_err(&pdev->dev, "failed to register wdt timer isr\n");
+		goto unregister_watchdog;
 	}
 
-	dev_info(&pdev->dev, " started, timer margin: %d sec\n",
-						WDT_DEFAULT_TIME);
+	dev_info(&pdev->dev,
+		"%s at MMIO 0x%p (timeout = %us, max_timeout = %us)",
+		dev_name(wdd->dev), hw->regs,
+		wdd->timeout, wdd->max_timeout);
 
 	return 0;
 
-unregister_timer:
-	bcm63xx_timer_unregister(TIMER_WDT_ID);
+unregister_watchdog:
+	watchdog_unregister_device(wdd);
 	return ret;
 }
 
 static int bcm63xx_wdt_remove(struct platform_device *pdev)
 {
-	if (!nowayout)
-		bcm63xx_wdt_hw_stop();
+	struct bcm63xx_wdt_hw *hw = platform_get_drvdata(pdev);
 
-	misc_deregister(&bcm63xx_wdt_miscdev);
 	bcm63xx_timer_unregister(TIMER_WDT_ID);
+	watchdog_unregister_device(&hw->wdd);
 	return 0;
 }
 
 static void bcm63xx_wdt_shutdown(struct platform_device *pdev)
 {
-	bcm63xx_wdt_hw_stop();
+	struct bcm63xx_wdt_hw *hw = platform_get_drvdata(pdev);
+
+	bcm63xx_wdt_stop(&hw->wdd);
 }
 
 static struct platform_driver bcm63xx_wdt_driver = {
-- 
2.1.4

-- 
Simon Arlott
--
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] 95+ messages in thread

* [PATCH (v3) 6/11] watchdog: bcm63xx_wdt: Obtain watchdog clock HZ from "periph" clk
@ 2015-11-25 22:47                   ` Simon Arlott
  0 siblings, 0 replies; 95+ messages in thread
From: Simon Arlott @ 2015-11-25 22:47 UTC (permalink / raw)
  To: Florian Fainelli, linux-watchdog
  Cc: Guenter Roeck, devicetree, Ralf Baechle, Thomas Gleixner,
	Jason Cooper, Marc Zyngier, Kevin Cernekee, Wim Van Sebroeck,
	Maxime Bizon, Linux Kernel Mailing List, linux-mips, Rob Herring,
	Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, Jonas Gorski

Instead of using a fixed clock HZ in the driver, obtain it from the
"periph" clk that the watchdog timer uses.

Signed-off-by: Simon Arlott <simon@fire.lp0.eu>
---
Patch 7 split into two patches.

On 24/11/15 22:42, Florian Fainelli wrote:
> On 24/11/15 14:12, Simon Arlott wrote:
>> Instead of using a fixed clock HZ in the driver, obtain it from the
>> "periph" clk that the watchdog timer uses.
>> 
>> Signed-off-by: Simon Arlott <simon@fire.lp0.eu>
> 
> Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
> 

Changed because of the reordering of timer/watchdog register calls.

 drivers/watchdog/bcm63xx_wdt.c | 36 +++++++++++++++++++++++++++++++-----
 1 file changed, 31 insertions(+), 5 deletions(-)

diff --git a/drivers/watchdog/bcm63xx_wdt.c b/drivers/watchdog/bcm63xx_wdt.c
index 2257924..0a19731 100644
--- a/drivers/watchdog/bcm63xx_wdt.c
+++ b/drivers/watchdog/bcm63xx_wdt.c
@@ -13,6 +13,7 @@
 
 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
 
+#include <linux/clk.h>
 #include <linux/errno.h>
 #include <linux/io.h>
 #include <linux/kernel.h>
@@ -32,12 +33,14 @@
 
 #define PFX KBUILD_MODNAME
 
-#define WDT_HZ			50000000		/* Fclk */
+#define WDT_CLK_NAME		"periph"
 
 struct bcm63xx_wdt_hw {
 	struct watchdog_device wdd;
 	raw_spinlock_t lock;
 	void __iomem *regs;
+	struct clk *clk;
+	unsigned long clock_hz;
 	bool running;
 };
 
@@ -54,7 +57,7 @@ static int bcm63xx_wdt_start(struct watchdog_device *wdd)
 	unsigned long flags;
 
 	raw_spin_lock_irqsave(&hw->lock, flags);
-	bcm_writel(wdd->timeout * WDT_HZ, hw->regs + WDT_DEFVAL_REG);
+	bcm_writel(wdd->timeout * hw->clock_hz, hw->regs + WDT_DEFVAL_REG);
 	bcm_writel(WDT_START_1, hw->regs + WDT_CTL_REG);
 	bcm_writel(WDT_START_2, hw->regs + WDT_CTL_REG);
 	hw->running = true;
@@ -118,7 +121,7 @@ static void bcm63xx_wdt_isr(void *data)
 			die(PFX ": watchdog timer expired\n", get_irq_regs());
 		}
 
-		ms = timeleft / (WDT_HZ / 1000);
+		ms = timeleft / (hw->clock_hz / 1000);
 		dev_alert(hw->wdd.dev,
 			"warning timer fired, reboot in %ums\n", ms);
 	}
@@ -162,6 +165,25 @@ static int bcm63xx_wdt_probe(struct platform_device *pdev)
 		return -ENXIO;
 	}
 
+	hw->clk = devm_clk_get(&pdev->dev, WDT_CLK_NAME);
+	if (IS_ERR(hw->clk)) {
+		if (PTR_ERR(hw->clk) != -EPROBE_DEFER)
+			dev_err(&pdev->dev, "unable to request clock\n");
+		return PTR_ERR(hw->clk);
+	}
+
+	hw->clock_hz = clk_get_rate(hw->clk);
+	if (!hw->clock_hz) {
+		dev_err(&pdev->dev, "unable to fetch clock rate\n");
+		return -EINVAL;
+	}
+
+	ret = clk_prepare_enable(hw->clk);
+	if (ret) {
+		dev_err(&pdev->dev, "unable to enable clock\n");
+		return ret;
+	}
+
 	raw_spin_lock_init(&hw->lock);
 	hw->running = false;
 
@@ -169,7 +191,7 @@ static int bcm63xx_wdt_probe(struct platform_device *pdev)
 	wdd->ops = &bcm63xx_wdt_ops;
 	wdd->info = &bcm63xx_wdt_info;
 	wdd->min_timeout = 1;
-	wdd->max_timeout = 0xffffffff / WDT_HZ;
+	wdd->max_timeout = 0xffffffff / hw->clock_hz;
 	wdd->timeout = min(30U, wdd->max_timeout);
 
 	platform_set_drvdata(pdev, hw);
@@ -180,7 +202,7 @@ static int bcm63xx_wdt_probe(struct platform_device *pdev)
 	ret = watchdog_register_device(wdd);
 	if (ret < 0) {
 		dev_err(&pdev->dev, "failed to register watchdog device\n");
-		return ret;
+		goto disable_clk;
 	}
 
 	ret = bcm63xx_timer_register(TIMER_WDT_ID, bcm63xx_wdt_isr, hw);
@@ -198,6 +220,9 @@ static int bcm63xx_wdt_probe(struct platform_device *pdev)
 
 unregister_watchdog:
 	watchdog_unregister_device(wdd);
+
+disable_clk:
+	clk_disable_unprepare(hw->clk);
 	return ret;
 }
 
@@ -207,6 +232,7 @@ static int bcm63xx_wdt_remove(struct platform_device *pdev)
 
 	bcm63xx_timer_unregister(TIMER_WDT_ID);
 	watchdog_unregister_device(&hw->wdd);
+	clk_disable_unprepare(hw->clk);
 	return 0;
 }
 
-- 
2.1.4

-- 
Simon Arlott

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

* [PATCH (v3) 6/11] watchdog: bcm63xx_wdt: Obtain watchdog clock HZ from "periph" clk
@ 2015-11-25 22:47                   ` Simon Arlott
  0 siblings, 0 replies; 95+ messages in thread
From: Simon Arlott @ 2015-11-25 22:47 UTC (permalink / raw)
  To: Florian Fainelli, linux-watchdog-u79uwXL29TY76Z2rM5mHXA
  Cc: Guenter Roeck, devicetree-u79uwXL29TY76Z2rM5mHXA, Ralf Baechle,
	Thomas Gleixner, Jason Cooper, Marc Zyngier, Kevin Cernekee,
	Wim Van Sebroeck, Maxime Bizon, Linux Kernel Mailing List,
	linux-mips-6z/3iImG2C8G8FEW9MqTrA, Rob Herring, Pawel Moll,
	Mark Rutland, Ian Campbell, Kumar Gala, Jonas Gorski

Instead of using a fixed clock HZ in the driver, obtain it from the
"periph" clk that the watchdog timer uses.

Signed-off-by: Simon Arlott <simon-A6De1vDTPLDsq35pWSNszA@public.gmane.org>
---
Patch 7 split into two patches.

On 24/11/15 22:42, Florian Fainelli wrote:
> On 24/11/15 14:12, Simon Arlott wrote:
>> Instead of using a fixed clock HZ in the driver, obtain it from the
>> "periph" clk that the watchdog timer uses.
>> 
>> Signed-off-by: Simon Arlott <simon-A6De1vDTPLDsq35pWSNszA@public.gmane.org>
> 
> Reviewed-by: Florian Fainelli <f.fainelli-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
> 

Changed because of the reordering of timer/watchdog register calls.

 drivers/watchdog/bcm63xx_wdt.c | 36 +++++++++++++++++++++++++++++++-----
 1 file changed, 31 insertions(+), 5 deletions(-)

diff --git a/drivers/watchdog/bcm63xx_wdt.c b/drivers/watchdog/bcm63xx_wdt.c
index 2257924..0a19731 100644
--- a/drivers/watchdog/bcm63xx_wdt.c
+++ b/drivers/watchdog/bcm63xx_wdt.c
@@ -13,6 +13,7 @@
 
 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
 
+#include <linux/clk.h>
 #include <linux/errno.h>
 #include <linux/io.h>
 #include <linux/kernel.h>
@@ -32,12 +33,14 @@
 
 #define PFX KBUILD_MODNAME
 
-#define WDT_HZ			50000000		/* Fclk */
+#define WDT_CLK_NAME		"periph"
 
 struct bcm63xx_wdt_hw {
 	struct watchdog_device wdd;
 	raw_spinlock_t lock;
 	void __iomem *regs;
+	struct clk *clk;
+	unsigned long clock_hz;
 	bool running;
 };
 
@@ -54,7 +57,7 @@ static int bcm63xx_wdt_start(struct watchdog_device *wdd)
 	unsigned long flags;
 
 	raw_spin_lock_irqsave(&hw->lock, flags);
-	bcm_writel(wdd->timeout * WDT_HZ, hw->regs + WDT_DEFVAL_REG);
+	bcm_writel(wdd->timeout * hw->clock_hz, hw->regs + WDT_DEFVAL_REG);
 	bcm_writel(WDT_START_1, hw->regs + WDT_CTL_REG);
 	bcm_writel(WDT_START_2, hw->regs + WDT_CTL_REG);
 	hw->running = true;
@@ -118,7 +121,7 @@ static void bcm63xx_wdt_isr(void *data)
 			die(PFX ": watchdog timer expired\n", get_irq_regs());
 		}
 
-		ms = timeleft / (WDT_HZ / 1000);
+		ms = timeleft / (hw->clock_hz / 1000);
 		dev_alert(hw->wdd.dev,
 			"warning timer fired, reboot in %ums\n", ms);
 	}
@@ -162,6 +165,25 @@ static int bcm63xx_wdt_probe(struct platform_device *pdev)
 		return -ENXIO;
 	}
 
+	hw->clk = devm_clk_get(&pdev->dev, WDT_CLK_NAME);
+	if (IS_ERR(hw->clk)) {
+		if (PTR_ERR(hw->clk) != -EPROBE_DEFER)
+			dev_err(&pdev->dev, "unable to request clock\n");
+		return PTR_ERR(hw->clk);
+	}
+
+	hw->clock_hz = clk_get_rate(hw->clk);
+	if (!hw->clock_hz) {
+		dev_err(&pdev->dev, "unable to fetch clock rate\n");
+		return -EINVAL;
+	}
+
+	ret = clk_prepare_enable(hw->clk);
+	if (ret) {
+		dev_err(&pdev->dev, "unable to enable clock\n");
+		return ret;
+	}
+
 	raw_spin_lock_init(&hw->lock);
 	hw->running = false;
 
@@ -169,7 +191,7 @@ static int bcm63xx_wdt_probe(struct platform_device *pdev)
 	wdd->ops = &bcm63xx_wdt_ops;
 	wdd->info = &bcm63xx_wdt_info;
 	wdd->min_timeout = 1;
-	wdd->max_timeout = 0xffffffff / WDT_HZ;
+	wdd->max_timeout = 0xffffffff / hw->clock_hz;
 	wdd->timeout = min(30U, wdd->max_timeout);
 
 	platform_set_drvdata(pdev, hw);
@@ -180,7 +202,7 @@ static int bcm63xx_wdt_probe(struct platform_device *pdev)
 	ret = watchdog_register_device(wdd);
 	if (ret < 0) {
 		dev_err(&pdev->dev, "failed to register watchdog device\n");
-		return ret;
+		goto disable_clk;
 	}
 
 	ret = bcm63xx_timer_register(TIMER_WDT_ID, bcm63xx_wdt_isr, hw);
@@ -198,6 +220,9 @@ static int bcm63xx_wdt_probe(struct platform_device *pdev)
 
 unregister_watchdog:
 	watchdog_unregister_device(wdd);
+
+disable_clk:
+	clk_disable_unprepare(hw->clk);
 	return ret;
 }
 
@@ -207,6 +232,7 @@ static int bcm63xx_wdt_remove(struct platform_device *pdev)
 
 	bcm63xx_timer_unregister(TIMER_WDT_ID);
 	watchdog_unregister_device(&hw->wdd);
+	clk_disable_unprepare(hw->clk);
 	return 0;
 }
 
-- 
2.1.4

-- 
Simon Arlott
--
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] 95+ messages in thread

* [PATCH (v3) 7/11] watchdog: bcm63xx_wdt: Add get_timeleft function
@ 2015-11-25 22:50                     ` Simon Arlott
  0 siblings, 0 replies; 95+ messages in thread
From: Simon Arlott @ 2015-11-25 22:50 UTC (permalink / raw)
  To: Guenter Roeck, linux-watchdog
  Cc: devicetree, Ralf Baechle, Thomas Gleixner, Jason Cooper,
	Marc Zyngier, Kevin Cernekee, Florian Fainelli, Wim Van Sebroeck,
	Maxime Bizon, Linux Kernel Mailing List, linux-mips, Rob Herring,
	Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, Jonas Gorski

Return the remaining time from the hardware control register.

Signed-off-by: Simon Arlott <simon@fire.lp0.eu>
---
On 25/11/15 02:51, Guenter Roeck wrote:
> This is really two logical changes, isn't it ?

Patch 7 split into two patches.

 drivers/watchdog/bcm63xx_wdt.c | 17 ++++++++++++++++-
 1 file changed, 16 insertions(+), 1 deletion(-)

diff --git a/drivers/watchdog/bcm63xx_wdt.c b/drivers/watchdog/bcm63xx_wdt.c
index 0a19731..5615277 100644
--- a/drivers/watchdog/bcm63xx_wdt.c
+++ b/drivers/watchdog/bcm63xx_wdt.c
@@ -14,6 +14,7 @@
 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
 
 #include <linux/clk.h>
+#include <linux/delay.h>
 #include <linux/errno.h>
 #include <linux/io.h>
 #include <linux/kernel.h>
@@ -78,6 +79,19 @@ static int bcm63xx_wdt_stop(struct watchdog_device *wdd)
 	return 0;
 }
 
+static unsigned int bcm63xx_wdt_get_timeleft(struct watchdog_device *wdd)
+{
+	struct bcm63xx_wdt_hw *hw = to_wdt_hw(wdd);
+	unsigned long flags;
+	u32 val;
+
+	raw_spin_lock_irqsave(&hw->lock, flags);
+	val = __raw_readl(hw->regs + WDT_CTL_REG);
+	val /= hw->clock_hz;
+	raw_spin_unlock_irqrestore(&hw->lock, flags);
+	return val;
+}
+
 static int bcm63xx_wdt_set_timeout(struct watchdog_device *wdd,
 	unsigned int timeout)
 {
@@ -132,6 +146,7 @@ static struct watchdog_ops bcm63xx_wdt_ops = {
 	.owner = THIS_MODULE,
 	.start = bcm63xx_wdt_start,
 	.stop = bcm63xx_wdt_stop,
+	.get_timeleft = bcm63xx_wdt_get_timeleft,
 	.set_timeout = bcm63xx_wdt_set_timeout,
 };
 
@@ -215,7 +230,6 @@ static int bcm63xx_wdt_probe(struct platform_device *pdev)
 		"%s at MMIO 0x%p (timeout = %us, max_timeout = %us)",
 		dev_name(wdd->dev), hw->regs,
 		wdd->timeout, wdd->max_timeout);
-
 	return 0;
 
 unregister_watchdog:
@@ -256,6 +270,7 @@ module_platform_driver(bcm63xx_wdt_driver);
 
 MODULE_AUTHOR("Miguel Gaio <miguel.gaio@efixo.com>");
 MODULE_AUTHOR("Florian Fainelli <florian@openwrt.org>");
+MODULE_AUTHOR("Simon Arlott");
 MODULE_DESCRIPTION("Driver for the Broadcom BCM63xx SoC watchdog");
 MODULE_LICENSE("GPL");
 MODULE_ALIAS("platform:bcm63xx-wdt");
-- 
2.1.4

-- 
Simon Arlott

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

* [PATCH (v3) 7/11] watchdog: bcm63xx_wdt: Add get_timeleft function
@ 2015-11-25 22:50                     ` Simon Arlott
  0 siblings, 0 replies; 95+ messages in thread
From: Simon Arlott @ 2015-11-25 22:50 UTC (permalink / raw)
  To: Guenter Roeck, linux-watchdog-u79uwXL29TY76Z2rM5mHXA
  Cc: devicetree-u79uwXL29TY76Z2rM5mHXA, Ralf Baechle, Thomas Gleixner,
	Jason Cooper, Marc Zyngier, Kevin Cernekee, Florian Fainelli,
	Wim Van Sebroeck, Maxime Bizon, Linux Kernel Mailing List,
	linux-mips-6z/3iImG2C8G8FEW9MqTrA, Rob Herring, Pawel Moll,
	Mark Rutland, Ian Campbell, Kumar Gala, Jonas Gorski

Return the remaining time from the hardware control register.

Signed-off-by: Simon Arlott <simon-A6De1vDTPLDsq35pWSNszA@public.gmane.org>
---
On 25/11/15 02:51, Guenter Roeck wrote:
> This is really two logical changes, isn't it ?

Patch 7 split into two patches.

 drivers/watchdog/bcm63xx_wdt.c | 17 ++++++++++++++++-
 1 file changed, 16 insertions(+), 1 deletion(-)

diff --git a/drivers/watchdog/bcm63xx_wdt.c b/drivers/watchdog/bcm63xx_wdt.c
index 0a19731..5615277 100644
--- a/drivers/watchdog/bcm63xx_wdt.c
+++ b/drivers/watchdog/bcm63xx_wdt.c
@@ -14,6 +14,7 @@
 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
 
 #include <linux/clk.h>
+#include <linux/delay.h>
 #include <linux/errno.h>
 #include <linux/io.h>
 #include <linux/kernel.h>
@@ -78,6 +79,19 @@ static int bcm63xx_wdt_stop(struct watchdog_device *wdd)
 	return 0;
 }
 
+static unsigned int bcm63xx_wdt_get_timeleft(struct watchdog_device *wdd)
+{
+	struct bcm63xx_wdt_hw *hw = to_wdt_hw(wdd);
+	unsigned long flags;
+	u32 val;
+
+	raw_spin_lock_irqsave(&hw->lock, flags);
+	val = __raw_readl(hw->regs + WDT_CTL_REG);
+	val /= hw->clock_hz;
+	raw_spin_unlock_irqrestore(&hw->lock, flags);
+	return val;
+}
+
 static int bcm63xx_wdt_set_timeout(struct watchdog_device *wdd,
 	unsigned int timeout)
 {
@@ -132,6 +146,7 @@ static struct watchdog_ops bcm63xx_wdt_ops = {
 	.owner = THIS_MODULE,
 	.start = bcm63xx_wdt_start,
 	.stop = bcm63xx_wdt_stop,
+	.get_timeleft = bcm63xx_wdt_get_timeleft,
 	.set_timeout = bcm63xx_wdt_set_timeout,
 };
 
@@ -215,7 +230,6 @@ static int bcm63xx_wdt_probe(struct platform_device *pdev)
 		"%s at MMIO 0x%p (timeout = %us, max_timeout = %us)",
 		dev_name(wdd->dev), hw->regs,
 		wdd->timeout, wdd->max_timeout);
-
 	return 0;
 
 unregister_watchdog:
@@ -256,6 +270,7 @@ module_platform_driver(bcm63xx_wdt_driver);
 
 MODULE_AUTHOR("Miguel Gaio <miguel.gaio-HH44TBFINEIAvxtiuMwx3w@public.gmane.org>");
 MODULE_AUTHOR("Florian Fainelli <florian-p3rKhJxN3npAfugRpC6u6w@public.gmane.org>");
+MODULE_AUTHOR("Simon Arlott");
 MODULE_DESCRIPTION("Driver for the Broadcom BCM63xx SoC watchdog");
 MODULE_LICENSE("GPL");
 MODULE_ALIAS("platform:bcm63xx-wdt");
-- 
2.1.4

-- 
Simon Arlott
--
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] 95+ messages in thread

* [PATCH (v4) 7/11] watchdog: bcm63xx_wdt: Add get_timeleft function
@ 2015-11-25 22:54                       ` Simon Arlott
  0 siblings, 0 replies; 95+ messages in thread
From: Simon Arlott @ 2015-11-25 22:54 UTC (permalink / raw)
  To: Guenter Roeck, linux-watchdog
  Cc: devicetree, Ralf Baechle, Thomas Gleixner, Jason Cooper,
	Marc Zyngier, Kevin Cernekee, Florian Fainelli, Wim Van Sebroeck,
	Maxime Bizon, Linux Kernel Mailing List, linux-mips, Rob Herring,
	Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, Jonas Gorski

Return the remaining time from the hardware control register.

Signed-off-by: Simon Arlott <simon@fire.lp0.eu>
---
On 25/11/15 02:51, Guenter Roeck wrote:
> This is really two logical changes, isn't it ?

Patch 7 correctly split into two patches this time.

 drivers/watchdog/bcm63xx_wdt.c | 15 +++++++++++++++
 1 file changed, 15 insertions(+)

diff --git a/drivers/watchdog/bcm63xx_wdt.c b/drivers/watchdog/bcm63xx_wdt.c
index 0a19731..ab4a794 100644
--- a/drivers/watchdog/bcm63xx_wdt.c
+++ b/drivers/watchdog/bcm63xx_wdt.c
@@ -78,6 +78,19 @@ static int bcm63xx_wdt_stop(struct watchdog_device *wdd)
 	return 0;
 }
 
+static unsigned int bcm63xx_wdt_get_timeleft(struct watchdog_device *wdd)
+{
+	struct bcm63xx_wdt_hw *hw = to_wdt_hw(wdd);
+	unsigned long flags;
+	u32 val;
+
+	raw_spin_lock_irqsave(&hw->lock, flags);
+	val = __raw_readl(hw->regs + WDT_CTL_REG);
+	val /= hw->clock_hz;
+	raw_spin_unlock_irqrestore(&hw->lock, flags);
+	return val;
+}
+
 static int bcm63xx_wdt_set_timeout(struct watchdog_device *wdd,
 	unsigned int timeout)
 {
@@ -132,6 +145,7 @@ static struct watchdog_ops bcm63xx_wdt_ops = {
 	.owner = THIS_MODULE,
 	.start = bcm63xx_wdt_start,
 	.stop = bcm63xx_wdt_stop,
+	.get_timeleft = bcm63xx_wdt_get_timeleft,
 	.set_timeout = bcm63xx_wdt_set_timeout,
 };
 
@@ -256,6 +270,7 @@ module_platform_driver(bcm63xx_wdt_driver);
 
 MODULE_AUTHOR("Miguel Gaio <miguel.gaio@efixo.com>");
 MODULE_AUTHOR("Florian Fainelli <florian@openwrt.org>");
+MODULE_AUTHOR("Simon Arlott");
 MODULE_DESCRIPTION("Driver for the Broadcom BCM63xx SoC watchdog");
 MODULE_LICENSE("GPL");
 MODULE_ALIAS("platform:bcm63xx-wdt");
-- 
2.1.4

-- 
Simon Arlott

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

* [PATCH (v4) 7/11] watchdog: bcm63xx_wdt: Add get_timeleft function
@ 2015-11-25 22:54                       ` Simon Arlott
  0 siblings, 0 replies; 95+ messages in thread
From: Simon Arlott @ 2015-11-25 22:54 UTC (permalink / raw)
  To: Guenter Roeck, linux-watchdog-u79uwXL29TY76Z2rM5mHXA
  Cc: devicetree-u79uwXL29TY76Z2rM5mHXA, Ralf Baechle, Thomas Gleixner,
	Jason Cooper, Marc Zyngier, Kevin Cernekee, Florian Fainelli,
	Wim Van Sebroeck, Maxime Bizon, Linux Kernel Mailing List,
	linux-mips-6z/3iImG2C8G8FEW9MqTrA, Rob Herring, Pawel Moll,
	Mark Rutland, Ian Campbell, Kumar Gala, Jonas Gorski

Return the remaining time from the hardware control register.

Signed-off-by: Simon Arlott <simon-A6De1vDTPLDsq35pWSNszA@public.gmane.org>
---
On 25/11/15 02:51, Guenter Roeck wrote:
> This is really two logical changes, isn't it ?

Patch 7 correctly split into two patches this time.

 drivers/watchdog/bcm63xx_wdt.c | 15 +++++++++++++++
 1 file changed, 15 insertions(+)

diff --git a/drivers/watchdog/bcm63xx_wdt.c b/drivers/watchdog/bcm63xx_wdt.c
index 0a19731..ab4a794 100644
--- a/drivers/watchdog/bcm63xx_wdt.c
+++ b/drivers/watchdog/bcm63xx_wdt.c
@@ -78,6 +78,19 @@ static int bcm63xx_wdt_stop(struct watchdog_device *wdd)
 	return 0;
 }
 
+static unsigned int bcm63xx_wdt_get_timeleft(struct watchdog_device *wdd)
+{
+	struct bcm63xx_wdt_hw *hw = to_wdt_hw(wdd);
+	unsigned long flags;
+	u32 val;
+
+	raw_spin_lock_irqsave(&hw->lock, flags);
+	val = __raw_readl(hw->regs + WDT_CTL_REG);
+	val /= hw->clock_hz;
+	raw_spin_unlock_irqrestore(&hw->lock, flags);
+	return val;
+}
+
 static int bcm63xx_wdt_set_timeout(struct watchdog_device *wdd,
 	unsigned int timeout)
 {
@@ -132,6 +145,7 @@ static struct watchdog_ops bcm63xx_wdt_ops = {
 	.owner = THIS_MODULE,
 	.start = bcm63xx_wdt_start,
 	.stop = bcm63xx_wdt_stop,
+	.get_timeleft = bcm63xx_wdt_get_timeleft,
 	.set_timeout = bcm63xx_wdt_set_timeout,
 };
 
@@ -256,6 +270,7 @@ module_platform_driver(bcm63xx_wdt_driver);
 
 MODULE_AUTHOR("Miguel Gaio <miguel.gaio-HH44TBFINEIAvxtiuMwx3w@public.gmane.org>");
 MODULE_AUTHOR("Florian Fainelli <florian-p3rKhJxN3npAfugRpC6u6w@public.gmane.org>");
+MODULE_AUTHOR("Simon Arlott");
 MODULE_DESCRIPTION("Driver for the Broadcom BCM63xx SoC watchdog");
 MODULE_LICENSE("GPL");
 MODULE_ALIAS("platform:bcm63xx-wdt");
-- 
2.1.4

-- 
Simon Arlott
--
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] 95+ messages in thread

* [PATCH (v4) 8/11] watchdog: bcm63xx_wdt: Warn if the watchdog is currently running
@ 2015-11-25 22:57                         ` Simon Arlott
  0 siblings, 0 replies; 95+ messages in thread
From: Simon Arlott @ 2015-11-25 22:57 UTC (permalink / raw)
  To: Guenter Roeck, linux-watchdog
  Cc: devicetree, Ralf Baechle, Thomas Gleixner, Jason Cooper,
	Marc Zyngier, Kevin Cernekee, Florian Fainelli, Wim Van Sebroeck,
	Maxime Bizon, Linux Kernel Mailing List, linux-mips, Rob Herring,
	Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, Jonas Gorski

Warn when the device is registered if the hardware watchdog is currently
running and report the remaining time left.

Signed-off-by: Simon Arlott <simon@fire.lp0.eu>
---
On 25/11/15 02:51, Guenter Roeck wrote:
> This is really two logical changes, isn't it ?

Patch 7 split into two patches.

 drivers/watchdog/bcm63xx_wdt.c | 23 ++++++++++++++++++++++-
 1 file changed, 22 insertions(+), 1 deletion(-)

diff --git a/drivers/watchdog/bcm63xx_wdt.c b/drivers/watchdog/bcm63xx_wdt.c
index ab4a794..2312dc2 100644
--- a/drivers/watchdog/bcm63xx_wdt.c
+++ b/drivers/watchdog/bcm63xx_wdt.c
@@ -14,6 +14,7 @@
 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
 
 #include <linux/clk.h>
+#include <linux/delay.h>
 #include <linux/errno.h>
 #include <linux/io.h>
 #include <linux/kernel.h>
@@ -159,6 +160,8 @@ static int bcm63xx_wdt_probe(struct platform_device *pdev)
 	struct bcm63xx_wdt_hw *hw;
 	struct watchdog_device *wdd;
 	struct resource *r;
+	u32 timeleft1, timeleft2;
+	unsigned int timeleft;
 	int ret;
 
 	hw = devm_kzalloc(&pdev->dev, sizeof(*hw), GFP_KERNEL);
@@ -199,7 +202,6 @@ static int bcm63xx_wdt_probe(struct platform_device *pdev)
 	}
 
 	raw_spin_lock_init(&hw->lock);
-	hw->running = false;
 
 	wdd->parent = &pdev->dev;
 	wdd->ops = &bcm63xx_wdt_ops;
@@ -213,6 +215,23 @@ static int bcm63xx_wdt_probe(struct platform_device *pdev)
 	watchdog_init_timeout(wdd, 0, &pdev->dev);
 	watchdog_set_nowayout(wdd, nowayout);
 
+	/* Compare two reads of the time left value, 2 clock ticks apart */
+	rmb();
+	timeleft1 = __raw_readl(hw->regs + WDT_CTL_REG);
+	udelay(DIV_ROUND_UP(1000000, hw->clock_hz / 2));
+	/* Ensure the register is read twice */
+	rmb();
+	timeleft2 = __raw_readl(hw->regs + WDT_CTL_REG);
+
+	/* If the time left is changing, the watchdog is running */
+	if (timeleft1 != timeleft2) {
+		hw->running = true;
+		timeleft = bcm63xx_wdt_get_timeleft(wdd);
+	} else {
+		hw->running = false;
+		timeleft = 0;
+	}
+
 	ret = watchdog_register_device(wdd);
 	if (ret < 0) {
 		dev_err(&pdev->dev, "failed to register watchdog device\n");
@@ -230,6 +249,8 @@ static int bcm63xx_wdt_probe(struct platform_device *pdev)
 		dev_name(wdd->dev), hw->regs,
 		wdd->timeout, wdd->max_timeout);
 
+	if (hw->running)
+		dev_alert(wdd->dev, "running, reboot in %us\n", timeleft);
 	return 0;
 
 unregister_watchdog:
-- 
2.1.4

-- 
Simon Arlott

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

* [PATCH (v4) 8/11] watchdog: bcm63xx_wdt: Warn if the watchdog is currently running
@ 2015-11-25 22:57                         ` Simon Arlott
  0 siblings, 0 replies; 95+ messages in thread
From: Simon Arlott @ 2015-11-25 22:57 UTC (permalink / raw)
  To: Guenter Roeck, linux-watchdog-u79uwXL29TY76Z2rM5mHXA
  Cc: devicetree-u79uwXL29TY76Z2rM5mHXA, Ralf Baechle, Thomas Gleixner,
	Jason Cooper, Marc Zyngier, Kevin Cernekee, Florian Fainelli,
	Wim Van Sebroeck, Maxime Bizon, Linux Kernel Mailing List,
	linux-mips-6z/3iImG2C8G8FEW9MqTrA, Rob Herring, Pawel Moll,
	Mark Rutland, Ian Campbell, Kumar Gala, Jonas Gorski

Warn when the device is registered if the hardware watchdog is currently
running and report the remaining time left.

Signed-off-by: Simon Arlott <simon-A6De1vDTPLDsq35pWSNszA@public.gmane.org>
---
On 25/11/15 02:51, Guenter Roeck wrote:
> This is really two logical changes, isn't it ?

Patch 7 split into two patches.

 drivers/watchdog/bcm63xx_wdt.c | 23 ++++++++++++++++++++++-
 1 file changed, 22 insertions(+), 1 deletion(-)

diff --git a/drivers/watchdog/bcm63xx_wdt.c b/drivers/watchdog/bcm63xx_wdt.c
index ab4a794..2312dc2 100644
--- a/drivers/watchdog/bcm63xx_wdt.c
+++ b/drivers/watchdog/bcm63xx_wdt.c
@@ -14,6 +14,7 @@
 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
 
 #include <linux/clk.h>
+#include <linux/delay.h>
 #include <linux/errno.h>
 #include <linux/io.h>
 #include <linux/kernel.h>
@@ -159,6 +160,8 @@ static int bcm63xx_wdt_probe(struct platform_device *pdev)
 	struct bcm63xx_wdt_hw *hw;
 	struct watchdog_device *wdd;
 	struct resource *r;
+	u32 timeleft1, timeleft2;
+	unsigned int timeleft;
 	int ret;
 
 	hw = devm_kzalloc(&pdev->dev, sizeof(*hw), GFP_KERNEL);
@@ -199,7 +202,6 @@ static int bcm63xx_wdt_probe(struct platform_device *pdev)
 	}
 
 	raw_spin_lock_init(&hw->lock);
-	hw->running = false;
 
 	wdd->parent = &pdev->dev;
 	wdd->ops = &bcm63xx_wdt_ops;
@@ -213,6 +215,23 @@ static int bcm63xx_wdt_probe(struct platform_device *pdev)
 	watchdog_init_timeout(wdd, 0, &pdev->dev);
 	watchdog_set_nowayout(wdd, nowayout);
 
+	/* Compare two reads of the time left value, 2 clock ticks apart */
+	rmb();
+	timeleft1 = __raw_readl(hw->regs + WDT_CTL_REG);
+	udelay(DIV_ROUND_UP(1000000, hw->clock_hz / 2));
+	/* Ensure the register is read twice */
+	rmb();
+	timeleft2 = __raw_readl(hw->regs + WDT_CTL_REG);
+
+	/* If the time left is changing, the watchdog is running */
+	if (timeleft1 != timeleft2) {
+		hw->running = true;
+		timeleft = bcm63xx_wdt_get_timeleft(wdd);
+	} else {
+		hw->running = false;
+		timeleft = 0;
+	}
+
 	ret = watchdog_register_device(wdd);
 	if (ret < 0) {
 		dev_err(&pdev->dev, "failed to register watchdog device\n");
@@ -230,6 +249,8 @@ static int bcm63xx_wdt_probe(struct platform_device *pdev)
 		dev_name(wdd->dev), hw->regs,
 		wdd->timeout, wdd->max_timeout);
 
+	if (hw->running)
+		dev_alert(wdd->dev, "running, reboot in %us\n", timeleft);
 	return 0;
 
 unregister_watchdog:
-- 
2.1.4

-- 
Simon Arlott
--
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] 95+ messages in thread

* [PATCH (v2) 10/11] watchdog: bcm63xx_wdt: Use bcm63xx_timer interrupt directly
@ 2015-11-25 23:03               ` Simon Arlott
  0 siblings, 0 replies; 95+ messages in thread
From: Simon Arlott @ 2015-11-25 23:03 UTC (permalink / raw)
  To: Guenter Roeck, devicetree, Ralf Baechle, Thomas Gleixner,
	Jason Cooper, Marc Zyngier, Kevin Cernekee, Florian Fainelli,
	Wim Van Sebroeck, Maxime Bizon, Linux Kernel Mailing List,
	linux-mips, linux-watchdog
  Cc: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
	Jonas Gorski

There is only one user of bcm63xx_timer and that is the watchdog.
To allow the watchdog driver to be used on machine types other than
mach-bcm63xx, it needs to use an interrupt instead of a custom register
function.

Modify bcm63xx_timer to only disable the timers (so that they don't
interfere with the watchdog if an interrupt occurs) and remove its
exported functions.

Use the timer interrupt directly in bcm63xx_wdt.

Signed-off-by: Simon Arlott <simon@fire.lp0.eu>
---
Patch 7 split into two patches.

There was no change to patch 8/10 which is now 9/11.

On 25/11/15 22:40, Simon Arlott wrote:
> Moved registration of the timer in the probe function to after register
> of the watchdog device because the interrupt handler uses wdd->dev.

This patch (9/10 now 10/11) was affected by this reordering, and an
earlier change to use "hw" instead of "wdd" as the interrupt data.

 arch/mips/bcm63xx/dev-wdt.c                        |   7 +
 arch/mips/bcm63xx/timer.c                          | 181 +--------------------
 arch/mips/include/asm/mach-bcm63xx/bcm63xx_timer.h |  11 --
 drivers/watchdog/bcm63xx_wdt.c                     |  41 +++--
 4 files changed, 36 insertions(+), 204 deletions(-)
 delete mode 100644 arch/mips/include/asm/mach-bcm63xx/bcm63xx_timer.h

diff --git a/arch/mips/bcm63xx/dev-wdt.c b/arch/mips/bcm63xx/dev-wdt.c
index 2a2346a..a7a5497 100644
--- a/arch/mips/bcm63xx/dev-wdt.c
+++ b/arch/mips/bcm63xx/dev-wdt.c
@@ -17,6 +17,11 @@ static struct resource wdt_resources[] = {
 		.end		= -1, /* filled at runtime */
 		.flags		= IORESOURCE_MEM,
 	},
+	{
+		.start		= -1, /* filled at runtime */
+		.end		= -1, /* filled at runtime */
+		.flags		= IORESOURCE_IRQ,
+	},
 };
 
 static struct platform_device bcm63xx_wdt_device = {
@@ -32,6 +37,8 @@ int __init bcm63xx_wdt_register(void)
 	wdt_resources[0].end = wdt_resources[0].start;
 	wdt_resources[0].end += RSET_WDT_SIZE - 1;
 
+	wdt_resources[1].start = bcm63xx_get_irq_number(IRQ_TIMER);
+
 	return platform_device_register(&bcm63xx_wdt_device);
 }
 arch_initcall(bcm63xx_wdt_register);
diff --git a/arch/mips/bcm63xx/timer.c b/arch/mips/bcm63xx/timer.c
index 2110359..9c7b41a6 100644
--- a/arch/mips/bcm63xx/timer.c
+++ b/arch/mips/bcm63xx/timer.c
@@ -9,196 +9,23 @@
 #include <linux/kernel.h>
 #include <linux/err.h>
 #include <linux/module.h>
-#include <linux/spinlock.h>
-#include <linux/interrupt.h>
-#include <linux/clk.h>
 #include <bcm63xx_cpu.h>
 #include <bcm63xx_io.h>
-#include <bcm63xx_timer.h>
 #include <bcm63xx_regs.h>
 
-static DEFINE_RAW_SPINLOCK(timer_reg_lock);
-static DEFINE_RAW_SPINLOCK(timer_data_lock);
-static struct clk *periph_clk;
-
-static struct timer_data {
-	void	(*cb)(void *);
-	void	*data;
-} timer_data[BCM63XX_TIMER_COUNT];
-
-static irqreturn_t timer_interrupt(int irq, void *dev_id)
-{
-	u32 stat;
-	int i;
-
-	raw_spin_lock(&timer_reg_lock);
-	stat = bcm_timer_readl(TIMER_IRQSTAT_REG);
-	bcm_timer_writel(stat, TIMER_IRQSTAT_REG);
-	raw_spin_unlock(&timer_reg_lock);
-
-	for (i = 0; i < BCM63XX_TIMER_COUNT; i++) {
-		if (!(stat & TIMER_IRQSTAT_TIMER_CAUSE(i)))
-			continue;
-
-		raw_spin_lock(&timer_data_lock);
-		if (!timer_data[i].cb) {
-			raw_spin_unlock(&timer_data_lock);
-			continue;
-		}
-
-		timer_data[i].cb(timer_data[i].data);
-		raw_spin_unlock(&timer_data_lock);
-	}
-
-	return IRQ_HANDLED;
-}
-
-int bcm63xx_timer_enable(int id)
-{
-	u32 reg;
-	unsigned long flags;
-
-	if (id >= BCM63XX_TIMER_COUNT)
-		return -EINVAL;
-
-	raw_spin_lock_irqsave(&timer_reg_lock, flags);
-
-	reg = bcm_timer_readl(TIMER_CTLx_REG(id));
-	reg |= TIMER_CTL_ENABLE_MASK;
-	bcm_timer_writel(reg, TIMER_CTLx_REG(id));
-
-	reg = bcm_timer_readl(TIMER_IRQSTAT_REG);
-	reg |= TIMER_IRQSTAT_TIMER_IR_EN(id);
-	bcm_timer_writel(reg, TIMER_IRQSTAT_REG);
-
-	raw_spin_unlock_irqrestore(&timer_reg_lock, flags);
-	return 0;
-}
-
-EXPORT_SYMBOL(bcm63xx_timer_enable);
-
-int bcm63xx_timer_disable(int id)
+static int bcm63xx_timer_init(void)
 {
 	u32 reg;
-	unsigned long flags;
-
-	if (id >= BCM63XX_TIMER_COUNT)
-		return -EINVAL;
-
-	raw_spin_lock_irqsave(&timer_reg_lock, flags);
-
-	reg = bcm_timer_readl(TIMER_CTLx_REG(id));
-	reg &= ~TIMER_CTL_ENABLE_MASK;
-	bcm_timer_writel(reg, TIMER_CTLx_REG(id));
-
-	reg = bcm_timer_readl(TIMER_IRQSTAT_REG);
-	reg &= ~TIMER_IRQSTAT_TIMER_IR_EN(id);
-	bcm_timer_writel(reg, TIMER_IRQSTAT_REG);
-
-	raw_spin_unlock_irqrestore(&timer_reg_lock, flags);
-	return 0;
-}
-
-EXPORT_SYMBOL(bcm63xx_timer_disable);
-
-int bcm63xx_timer_register(int id, void (*callback)(void *data), void *data)
-{
-	unsigned long flags;
-	int ret;
-
-	if (id >= BCM63XX_TIMER_COUNT || !callback)
-		return -EINVAL;
-
-	ret = 0;
-	raw_spin_lock_irqsave(&timer_data_lock, flags);
-	if (timer_data[id].cb) {
-		ret = -EBUSY;
-		goto out;
-	}
-
-	timer_data[id].cb = callback;
-	timer_data[id].data = data;
-
-out:
-	raw_spin_unlock_irqrestore(&timer_data_lock, flags);
-	return ret;
-}
-
-EXPORT_SYMBOL(bcm63xx_timer_register);
-
-void bcm63xx_timer_unregister(int id)
-{
-	unsigned long flags;
-
-	if (id >= BCM63XX_TIMER_COUNT)
-		return;
-
-	raw_spin_lock_irqsave(&timer_data_lock, flags);
-	timer_data[id].cb = NULL;
-	raw_spin_unlock_irqrestore(&timer_data_lock, flags);
-}
-
-EXPORT_SYMBOL(bcm63xx_timer_unregister);
-
-unsigned int bcm63xx_timer_countdown(unsigned int countdown_us)
-{
-	return (clk_get_rate(periph_clk) / (1000 * 1000)) * countdown_us;
-}
-
-EXPORT_SYMBOL(bcm63xx_timer_countdown);
-
-int bcm63xx_timer_set(int id, int monotonic, unsigned int countdown_us)
-{
-	u32 reg, countdown;
-	unsigned long flags;
-
-	if (id >= BCM63XX_TIMER_COUNT)
-		return -EINVAL;
-
-	countdown = bcm63xx_timer_countdown(countdown_us);
-	if (countdown & ~TIMER_CTL_COUNTDOWN_MASK)
-		return -EINVAL;
-
-	raw_spin_lock_irqsave(&timer_reg_lock, flags);
-	reg = bcm_timer_readl(TIMER_CTLx_REG(id));
-
-	if (monotonic)
-		reg &= ~TIMER_CTL_MONOTONIC_MASK;
-	else
-		reg |= TIMER_CTL_MONOTONIC_MASK;
-
-	reg &= ~TIMER_CTL_COUNTDOWN_MASK;
-	reg |= countdown;
-	bcm_timer_writel(reg, TIMER_CTLx_REG(id));
-
-	raw_spin_unlock_irqrestore(&timer_reg_lock, flags);
-	return 0;
-}
-
-EXPORT_SYMBOL(bcm63xx_timer_set);
-
-int bcm63xx_timer_init(void)
-{
-	int ret, irq;
-	u32 reg;
 
+	/* Disable all timers so that they won't interfere with use of the
+	 * timer interrupt by the watchdog.
+	 */
 	reg = bcm_timer_readl(TIMER_IRQSTAT_REG);
 	reg &= ~TIMER_IRQSTAT_TIMER0_IR_EN;
 	reg &= ~TIMER_IRQSTAT_TIMER1_IR_EN;
 	reg &= ~TIMER_IRQSTAT_TIMER2_IR_EN;
 	bcm_timer_writel(reg, TIMER_IRQSTAT_REG);
 
-	periph_clk = clk_get(NULL, "periph");
-	if (IS_ERR(periph_clk))
-		return -ENODEV;
-
-	irq = bcm63xx_get_irq_number(IRQ_TIMER);
-	ret = request_irq(irq, timer_interrupt, 0, "bcm63xx_timer", NULL);
-	if (ret) {
-		pr_err("%s: failed to register irq\n", __func__);
-		return ret;
-	}
-
 	return 0;
 }
 
diff --git a/arch/mips/include/asm/mach-bcm63xx/bcm63xx_timer.h b/arch/mips/include/asm/mach-bcm63xx/bcm63xx_timer.h
deleted file mode 100644
index c0fce83..0000000
--- a/arch/mips/include/asm/mach-bcm63xx/bcm63xx_timer.h
+++ /dev/null
@@ -1,11 +0,0 @@
-#ifndef BCM63XX_TIMER_H_
-#define BCM63XX_TIMER_H_
-
-int bcm63xx_timer_register(int id, void (*callback)(void *data), void *data);
-void bcm63xx_timer_unregister(int id);
-int bcm63xx_timer_set(int id, int monotonic, unsigned int countdown_us);
-int bcm63xx_timer_enable(int id);
-int bcm63xx_timer_disable(int id);
-unsigned int bcm63xx_timer_countdown(unsigned int countdown_us);
-
-#endif /* !BCM63XX_TIMER_H_ */
diff --git a/drivers/watchdog/bcm63xx_wdt.c b/drivers/watchdog/bcm63xx_wdt.c
index f409523..fa6c28b 100644
--- a/drivers/watchdog/bcm63xx_wdt.c
+++ b/drivers/watchdog/bcm63xx_wdt.c
@@ -17,6 +17,7 @@
 #include <linux/clk.h>
 #include <linux/delay.h>
 #include <linux/errno.h>
+#include <linux/interrupt.h>
 #include <linux/io.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
@@ -28,9 +29,6 @@
 #include <linux/resource.h>
 #include <linux/platform_device.h>
 
-#include <bcm63xx_regs.h>
-#include <bcm63xx_timer.h>
-
 #define PFX KBUILD_MODNAME
 
 #define WDT_CLK_NAME		"periph"
@@ -41,6 +39,7 @@ struct bcm63xx_wdt_hw {
 	void __iomem *regs;
 	struct clk *clk;
 	unsigned long clock_hz;
+	int irq;
 	bool running;
 };
 
@@ -99,7 +98,7 @@ static int bcm63xx_wdt_set_timeout(struct watchdog_device *wdd,
 }
 
 /* The watchdog interrupt occurs when half the timeout is remaining */
-static void bcm63xx_wdt_isr(void *data)
+static irqreturn_t bcm63xx_wdt_interrupt(int irq, void *data)
 {
 	struct bcm63xx_wdt_hw *hw = data;
 	unsigned long flags;
@@ -139,6 +138,7 @@ static void bcm63xx_wdt_isr(void *data)
 			"warning timer fired, reboot in %ums\n", ms);
 	}
 	raw_spin_unlock_irqrestore(&hw->lock, flags);
+	return IRQ_HANDLED;
 }
 
 static struct watchdog_ops bcm63xx_wdt_ops = {
@@ -237,24 +237,31 @@ static int bcm63xx_wdt_probe(struct platform_device *pdev)
 		goto disable_clk;
 	}
 
-	ret = bcm63xx_timer_register(TIMER_WDT_ID, bcm63xx_wdt_isr, hw);
-	if (ret < 0) {
-		dev_err(&pdev->dev, "failed to register wdt timer isr\n");
-		goto unregister_watchdog;
+	hw->irq = platform_get_irq(pdev, 0);
+	if (hw->irq >= 0) {
+		ret = devm_request_irq(&pdev->dev, hw->irq,
+			bcm63xx_wdt_interrupt, IRQF_TIMER,
+			dev_name(&pdev->dev), hw);
+		if (ret)
+			hw->irq = -1;
 	}
 
-	dev_info(&pdev->dev,
-		"%s at MMIO 0x%p (timeout = %us, max_timeout = %us)",
-		dev_name(wdd->dev), hw->regs,
-		wdd->timeout, wdd->max_timeout);
+	if (hw->irq >= 0) {
+		dev_info(&pdev->dev,
+			"%s at MMIO 0x%p (irq = %d, timeout = %us, max_timeout = %us)",
+			dev_name(wdd->dev), hw->regs, hw->irq,
+			wdd->timeout, wdd->max_timeout);
+	} else {
+		dev_info(&pdev->dev,
+			"%s at MMIO 0x%p (timeout = %us, max_timeout = %us)",
+			dev_name(wdd->dev), hw->regs,
+			wdd->timeout, wdd->max_timeout);
+	}
 
 	if (hw->running)
 		dev_alert(wdd->dev, "running, reboot in %us\n", timeleft);
 	return 0;
 
-unregister_watchdog:
-	watchdog_unregister_device(wdd);
-
 disable_clk:
 	clk_disable_unprepare(hw->clk);
 	return ret;
@@ -264,7 +271,9 @@ static int bcm63xx_wdt_remove(struct platform_device *pdev)
 {
 	struct bcm63xx_wdt_hw *hw = platform_get_drvdata(pdev);
 
-	bcm63xx_timer_unregister(TIMER_WDT_ID);
+	if (hw->irq >= 0)
+		devm_free_irq(&pdev->dev, hw->irq, hw);
+
 	watchdog_unregister_device(&hw->wdd);
 	clk_disable_unprepare(hw->clk);
 	return 0;
-- 
2.1.4

-- 
Simon Arlott

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

* [PATCH (v2) 10/11] watchdog: bcm63xx_wdt: Use bcm63xx_timer interrupt directly
@ 2015-11-25 23:03               ` Simon Arlott
  0 siblings, 0 replies; 95+ messages in thread
From: Simon Arlott @ 2015-11-25 23:03 UTC (permalink / raw)
  To: Guenter Roeck, devicetree-u79uwXL29TY76Z2rM5mHXA, Ralf Baechle,
	Thomas Gleixner, Jason Cooper, Marc Zyngier, Kevin Cernekee,
	Florian Fainelli, Wim Van Sebroeck, Maxime Bizon,
	Linux Kernel Mailing List, linux-mips-6z/3iImG2C8G8FEW9MqTrA,
	linux-watchdog-u79uwXL29TY76Z2rM5mHXA
  Cc: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
	Jonas Gorski

There is only one user of bcm63xx_timer and that is the watchdog.
To allow the watchdog driver to be used on machine types other than
mach-bcm63xx, it needs to use an interrupt instead of a custom register
function.

Modify bcm63xx_timer to only disable the timers (so that they don't
interfere with the watchdog if an interrupt occurs) and remove its
exported functions.

Use the timer interrupt directly in bcm63xx_wdt.

Signed-off-by: Simon Arlott <simon-A6De1vDTPLDsq35pWSNszA@public.gmane.org>
---
Patch 7 split into two patches.

There was no change to patch 8/10 which is now 9/11.

On 25/11/15 22:40, Simon Arlott wrote:
> Moved registration of the timer in the probe function to after register
> of the watchdog device because the interrupt handler uses wdd->dev.

This patch (9/10 now 10/11) was affected by this reordering, and an
earlier change to use "hw" instead of "wdd" as the interrupt data.

 arch/mips/bcm63xx/dev-wdt.c                        |   7 +
 arch/mips/bcm63xx/timer.c                          | 181 +--------------------
 arch/mips/include/asm/mach-bcm63xx/bcm63xx_timer.h |  11 --
 drivers/watchdog/bcm63xx_wdt.c                     |  41 +++--
 4 files changed, 36 insertions(+), 204 deletions(-)
 delete mode 100644 arch/mips/include/asm/mach-bcm63xx/bcm63xx_timer.h

diff --git a/arch/mips/bcm63xx/dev-wdt.c b/arch/mips/bcm63xx/dev-wdt.c
index 2a2346a..a7a5497 100644
--- a/arch/mips/bcm63xx/dev-wdt.c
+++ b/arch/mips/bcm63xx/dev-wdt.c
@@ -17,6 +17,11 @@ static struct resource wdt_resources[] = {
 		.end		= -1, /* filled at runtime */
 		.flags		= IORESOURCE_MEM,
 	},
+	{
+		.start		= -1, /* filled at runtime */
+		.end		= -1, /* filled at runtime */
+		.flags		= IORESOURCE_IRQ,
+	},
 };
 
 static struct platform_device bcm63xx_wdt_device = {
@@ -32,6 +37,8 @@ int __init bcm63xx_wdt_register(void)
 	wdt_resources[0].end = wdt_resources[0].start;
 	wdt_resources[0].end += RSET_WDT_SIZE - 1;
 
+	wdt_resources[1].start = bcm63xx_get_irq_number(IRQ_TIMER);
+
 	return platform_device_register(&bcm63xx_wdt_device);
 }
 arch_initcall(bcm63xx_wdt_register);
diff --git a/arch/mips/bcm63xx/timer.c b/arch/mips/bcm63xx/timer.c
index 2110359..9c7b41a6 100644
--- a/arch/mips/bcm63xx/timer.c
+++ b/arch/mips/bcm63xx/timer.c
@@ -9,196 +9,23 @@
 #include <linux/kernel.h>
 #include <linux/err.h>
 #include <linux/module.h>
-#include <linux/spinlock.h>
-#include <linux/interrupt.h>
-#include <linux/clk.h>
 #include <bcm63xx_cpu.h>
 #include <bcm63xx_io.h>
-#include <bcm63xx_timer.h>
 #include <bcm63xx_regs.h>
 
-static DEFINE_RAW_SPINLOCK(timer_reg_lock);
-static DEFINE_RAW_SPINLOCK(timer_data_lock);
-static struct clk *periph_clk;
-
-static struct timer_data {
-	void	(*cb)(void *);
-	void	*data;
-} timer_data[BCM63XX_TIMER_COUNT];
-
-static irqreturn_t timer_interrupt(int irq, void *dev_id)
-{
-	u32 stat;
-	int i;
-
-	raw_spin_lock(&timer_reg_lock);
-	stat = bcm_timer_readl(TIMER_IRQSTAT_REG);
-	bcm_timer_writel(stat, TIMER_IRQSTAT_REG);
-	raw_spin_unlock(&timer_reg_lock);
-
-	for (i = 0; i < BCM63XX_TIMER_COUNT; i++) {
-		if (!(stat & TIMER_IRQSTAT_TIMER_CAUSE(i)))
-			continue;
-
-		raw_spin_lock(&timer_data_lock);
-		if (!timer_data[i].cb) {
-			raw_spin_unlock(&timer_data_lock);
-			continue;
-		}
-
-		timer_data[i].cb(timer_data[i].data);
-		raw_spin_unlock(&timer_data_lock);
-	}
-
-	return IRQ_HANDLED;
-}
-
-int bcm63xx_timer_enable(int id)
-{
-	u32 reg;
-	unsigned long flags;
-
-	if (id >= BCM63XX_TIMER_COUNT)
-		return -EINVAL;
-
-	raw_spin_lock_irqsave(&timer_reg_lock, flags);
-
-	reg = bcm_timer_readl(TIMER_CTLx_REG(id));
-	reg |= TIMER_CTL_ENABLE_MASK;
-	bcm_timer_writel(reg, TIMER_CTLx_REG(id));
-
-	reg = bcm_timer_readl(TIMER_IRQSTAT_REG);
-	reg |= TIMER_IRQSTAT_TIMER_IR_EN(id);
-	bcm_timer_writel(reg, TIMER_IRQSTAT_REG);
-
-	raw_spin_unlock_irqrestore(&timer_reg_lock, flags);
-	return 0;
-}
-
-EXPORT_SYMBOL(bcm63xx_timer_enable);
-
-int bcm63xx_timer_disable(int id)
+static int bcm63xx_timer_init(void)
 {
 	u32 reg;
-	unsigned long flags;
-
-	if (id >= BCM63XX_TIMER_COUNT)
-		return -EINVAL;
-
-	raw_spin_lock_irqsave(&timer_reg_lock, flags);
-
-	reg = bcm_timer_readl(TIMER_CTLx_REG(id));
-	reg &= ~TIMER_CTL_ENABLE_MASK;
-	bcm_timer_writel(reg, TIMER_CTLx_REG(id));
-
-	reg = bcm_timer_readl(TIMER_IRQSTAT_REG);
-	reg &= ~TIMER_IRQSTAT_TIMER_IR_EN(id);
-	bcm_timer_writel(reg, TIMER_IRQSTAT_REG);
-
-	raw_spin_unlock_irqrestore(&timer_reg_lock, flags);
-	return 0;
-}
-
-EXPORT_SYMBOL(bcm63xx_timer_disable);
-
-int bcm63xx_timer_register(int id, void (*callback)(void *data), void *data)
-{
-	unsigned long flags;
-	int ret;
-
-	if (id >= BCM63XX_TIMER_COUNT || !callback)
-		return -EINVAL;
-
-	ret = 0;
-	raw_spin_lock_irqsave(&timer_data_lock, flags);
-	if (timer_data[id].cb) {
-		ret = -EBUSY;
-		goto out;
-	}
-
-	timer_data[id].cb = callback;
-	timer_data[id].data = data;
-
-out:
-	raw_spin_unlock_irqrestore(&timer_data_lock, flags);
-	return ret;
-}
-
-EXPORT_SYMBOL(bcm63xx_timer_register);
-
-void bcm63xx_timer_unregister(int id)
-{
-	unsigned long flags;
-
-	if (id >= BCM63XX_TIMER_COUNT)
-		return;
-
-	raw_spin_lock_irqsave(&timer_data_lock, flags);
-	timer_data[id].cb = NULL;
-	raw_spin_unlock_irqrestore(&timer_data_lock, flags);
-}
-
-EXPORT_SYMBOL(bcm63xx_timer_unregister);
-
-unsigned int bcm63xx_timer_countdown(unsigned int countdown_us)
-{
-	return (clk_get_rate(periph_clk) / (1000 * 1000)) * countdown_us;
-}
-
-EXPORT_SYMBOL(bcm63xx_timer_countdown);
-
-int bcm63xx_timer_set(int id, int monotonic, unsigned int countdown_us)
-{
-	u32 reg, countdown;
-	unsigned long flags;
-
-	if (id >= BCM63XX_TIMER_COUNT)
-		return -EINVAL;
-
-	countdown = bcm63xx_timer_countdown(countdown_us);
-	if (countdown & ~TIMER_CTL_COUNTDOWN_MASK)
-		return -EINVAL;
-
-	raw_spin_lock_irqsave(&timer_reg_lock, flags);
-	reg = bcm_timer_readl(TIMER_CTLx_REG(id));
-
-	if (monotonic)
-		reg &= ~TIMER_CTL_MONOTONIC_MASK;
-	else
-		reg |= TIMER_CTL_MONOTONIC_MASK;
-
-	reg &= ~TIMER_CTL_COUNTDOWN_MASK;
-	reg |= countdown;
-	bcm_timer_writel(reg, TIMER_CTLx_REG(id));
-
-	raw_spin_unlock_irqrestore(&timer_reg_lock, flags);
-	return 0;
-}
-
-EXPORT_SYMBOL(bcm63xx_timer_set);
-
-int bcm63xx_timer_init(void)
-{
-	int ret, irq;
-	u32 reg;
 
+	/* Disable all timers so that they won't interfere with use of the
+	 * timer interrupt by the watchdog.
+	 */
 	reg = bcm_timer_readl(TIMER_IRQSTAT_REG);
 	reg &= ~TIMER_IRQSTAT_TIMER0_IR_EN;
 	reg &= ~TIMER_IRQSTAT_TIMER1_IR_EN;
 	reg &= ~TIMER_IRQSTAT_TIMER2_IR_EN;
 	bcm_timer_writel(reg, TIMER_IRQSTAT_REG);
 
-	periph_clk = clk_get(NULL, "periph");
-	if (IS_ERR(periph_clk))
-		return -ENODEV;
-
-	irq = bcm63xx_get_irq_number(IRQ_TIMER);
-	ret = request_irq(irq, timer_interrupt, 0, "bcm63xx_timer", NULL);
-	if (ret) {
-		pr_err("%s: failed to register irq\n", __func__);
-		return ret;
-	}
-
 	return 0;
 }
 
diff --git a/arch/mips/include/asm/mach-bcm63xx/bcm63xx_timer.h b/arch/mips/include/asm/mach-bcm63xx/bcm63xx_timer.h
deleted file mode 100644
index c0fce83..0000000
--- a/arch/mips/include/asm/mach-bcm63xx/bcm63xx_timer.h
+++ /dev/null
@@ -1,11 +0,0 @@
-#ifndef BCM63XX_TIMER_H_
-#define BCM63XX_TIMER_H_
-
-int bcm63xx_timer_register(int id, void (*callback)(void *data), void *data);
-void bcm63xx_timer_unregister(int id);
-int bcm63xx_timer_set(int id, int monotonic, unsigned int countdown_us);
-int bcm63xx_timer_enable(int id);
-int bcm63xx_timer_disable(int id);
-unsigned int bcm63xx_timer_countdown(unsigned int countdown_us);
-
-#endif /* !BCM63XX_TIMER_H_ */
diff --git a/drivers/watchdog/bcm63xx_wdt.c b/drivers/watchdog/bcm63xx_wdt.c
index f409523..fa6c28b 100644
--- a/drivers/watchdog/bcm63xx_wdt.c
+++ b/drivers/watchdog/bcm63xx_wdt.c
@@ -17,6 +17,7 @@
 #include <linux/clk.h>
 #include <linux/delay.h>
 #include <linux/errno.h>
+#include <linux/interrupt.h>
 #include <linux/io.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
@@ -28,9 +29,6 @@
 #include <linux/resource.h>
 #include <linux/platform_device.h>
 
-#include <bcm63xx_regs.h>
-#include <bcm63xx_timer.h>
-
 #define PFX KBUILD_MODNAME
 
 #define WDT_CLK_NAME		"periph"
@@ -41,6 +39,7 @@ struct bcm63xx_wdt_hw {
 	void __iomem *regs;
 	struct clk *clk;
 	unsigned long clock_hz;
+	int irq;
 	bool running;
 };
 
@@ -99,7 +98,7 @@ static int bcm63xx_wdt_set_timeout(struct watchdog_device *wdd,
 }
 
 /* The watchdog interrupt occurs when half the timeout is remaining */
-static void bcm63xx_wdt_isr(void *data)
+static irqreturn_t bcm63xx_wdt_interrupt(int irq, void *data)
 {
 	struct bcm63xx_wdt_hw *hw = data;
 	unsigned long flags;
@@ -139,6 +138,7 @@ static void bcm63xx_wdt_isr(void *data)
 			"warning timer fired, reboot in %ums\n", ms);
 	}
 	raw_spin_unlock_irqrestore(&hw->lock, flags);
+	return IRQ_HANDLED;
 }
 
 static struct watchdog_ops bcm63xx_wdt_ops = {
@@ -237,24 +237,31 @@ static int bcm63xx_wdt_probe(struct platform_device *pdev)
 		goto disable_clk;
 	}
 
-	ret = bcm63xx_timer_register(TIMER_WDT_ID, bcm63xx_wdt_isr, hw);
-	if (ret < 0) {
-		dev_err(&pdev->dev, "failed to register wdt timer isr\n");
-		goto unregister_watchdog;
+	hw->irq = platform_get_irq(pdev, 0);
+	if (hw->irq >= 0) {
+		ret = devm_request_irq(&pdev->dev, hw->irq,
+			bcm63xx_wdt_interrupt, IRQF_TIMER,
+			dev_name(&pdev->dev), hw);
+		if (ret)
+			hw->irq = -1;
 	}
 
-	dev_info(&pdev->dev,
-		"%s at MMIO 0x%p (timeout = %us, max_timeout = %us)",
-		dev_name(wdd->dev), hw->regs,
-		wdd->timeout, wdd->max_timeout);
+	if (hw->irq >= 0) {
+		dev_info(&pdev->dev,
+			"%s at MMIO 0x%p (irq = %d, timeout = %us, max_timeout = %us)",
+			dev_name(wdd->dev), hw->regs, hw->irq,
+			wdd->timeout, wdd->max_timeout);
+	} else {
+		dev_info(&pdev->dev,
+			"%s at MMIO 0x%p (timeout = %us, max_timeout = %us)",
+			dev_name(wdd->dev), hw->regs,
+			wdd->timeout, wdd->max_timeout);
+	}
 
 	if (hw->running)
 		dev_alert(wdd->dev, "running, reboot in %us\n", timeleft);
 	return 0;
 
-unregister_watchdog:
-	watchdog_unregister_device(wdd);
-
 disable_clk:
 	clk_disable_unprepare(hw->clk);
 	return ret;
@@ -264,7 +271,9 @@ static int bcm63xx_wdt_remove(struct platform_device *pdev)
 {
 	struct bcm63xx_wdt_hw *hw = platform_get_drvdata(pdev);
 
-	bcm63xx_timer_unregister(TIMER_WDT_ID);
+	if (hw->irq >= 0)
+		devm_free_irq(&pdev->dev, hw->irq, hw);
+
 	watchdog_unregister_device(&hw->wdd);
 	clk_disable_unprepare(hw->clk);
 	return 0;
-- 
2.1.4

-- 
Simon Arlott
--
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] 95+ messages in thread

* [PATCH (v2) 11/11] watchdog: bcm63xx_wdt: Use brcm,bcm6345-wdt device tree binding
@ 2015-11-25 23:09               ` Simon Arlott
  0 siblings, 0 replies; 95+ messages in thread
From: Simon Arlott @ 2015-11-25 23:09 UTC (permalink / raw)
  To: Guenter Roeck, devicetree, Ralf Baechle, Thomas Gleixner,
	Jason Cooper, Marc Zyngier, Kevin Cernekee, Florian Fainelli,
	Wim Van Sebroeck, Maxime Bizon, Linux Kernel Mailing List,
	linux-mips, linux-watchdog
  Cc: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
	Jonas Gorski

Add of_match_table for "brcm,bcm6345-wdt".

Use a NULL clock name when not on mach-bcm63xx so that the device tree
clock name does not have to be "periph".

Allow the watchdog to be selected on BMIPS_GENERIC and select the BCM6345
timer interrupt handler.

Signed-off-by: Simon Arlott <simon@fire.lp0.eu>
---
Patch 7 split into two patches.

On 25/11/15 02:44, Guenter Roeck wrote:
>> +	hw = devm_kzalloc(&pdev->dev, sizeof(*hw), GFP_KERNEL);
>> +	wdd = devm_kzalloc(&pdev->dev, sizeof(*wdd), GFP_KERNEL);
> 
> It would be better to allocate wdd as part of struct bcm63xx_wdt_hw.

This altered the context of this patch (was 10/10 now 11/11) because
platform_get_drvdata() is now "hw" instead of "wdd".

 drivers/watchdog/Kconfig       |  3 ++-
 drivers/watchdog/bcm63xx_wdt.c | 14 +++++++++++++-
 2 files changed, 15 insertions(+), 2 deletions(-)

diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index 6815b74..0c50add 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -1272,8 +1272,9 @@ config OCTEON_WDT
 
 config BCM63XX_WDT
 	tristate "Broadcom BCM63xx hardware watchdog"
-	depends on BCM63XX
+	depends on BCM63XX || BMIPS_GENERIC
 	select WATCHDOG_CORE
+	select BCM6345_L2_TIMER_IRQ if BMIPS_GENERIC
 	help
 	  Watchdog driver for the built in watchdog hardware in Broadcom
 	  BCM63xx SoC.
diff --git a/drivers/watchdog/bcm63xx_wdt.c b/drivers/watchdog/bcm63xx_wdt.c
index fa6c28b..4db4145 100644
--- a/drivers/watchdog/bcm63xx_wdt.c
+++ b/drivers/watchdog/bcm63xx_wdt.c
@@ -22,6 +22,7 @@
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/moduleparam.h>
+#include <linux/mod_devicetable.h>
 #include <linux/spinlock.h>
 #include <linux/types.h>
 #include <linux/watchdog.h>
@@ -31,7 +32,11 @@
 
 #define PFX KBUILD_MODNAME
 
-#define WDT_CLK_NAME		"periph"
+#ifdef CONFIG_BCM63XX
+# define WDT_CLK_NAME		"periph"
+#else
+# define WDT_CLK_NAME		NULL
+#endif
 
 struct bcm63xx_wdt_hw {
 	struct watchdog_device wdd;
@@ -286,12 +291,19 @@ static void bcm63xx_wdt_shutdown(struct platform_device *pdev)
 	bcm63xx_wdt_stop(&hw->wdd);
 }
 
+static const struct of_device_id bcm63xx_wdt_dt_ids[] = {
+	{ .compatible = "brcm,bcm6345-wdt" },
+	{}
+};
+MODULE_DEVICE_TABLE(of, bcm63xx_wdt_dt_ids);
+
 static struct platform_driver bcm63xx_wdt_driver = {
 	.probe	= bcm63xx_wdt_probe,
 	.remove = bcm63xx_wdt_remove,
 	.shutdown = bcm63xx_wdt_shutdown,
 	.driver = {
 		.name = "bcm63xx-wdt",
+		.of_match_table = bcm63xx_wdt_dt_ids,
 	}
 };
 
-- 
2.1.4

-- 
Simon Arlott

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

* [PATCH (v2) 11/11] watchdog: bcm63xx_wdt: Use brcm,bcm6345-wdt device tree binding
@ 2015-11-25 23:09               ` Simon Arlott
  0 siblings, 0 replies; 95+ messages in thread
From: Simon Arlott @ 2015-11-25 23:09 UTC (permalink / raw)
  To: Guenter Roeck, devicetree-u79uwXL29TY76Z2rM5mHXA, Ralf Baechle,
	Thomas Gleixner, Jason Cooper, Marc Zyngier, Kevin Cernekee,
	Florian Fainelli, Wim Van Sebroeck, Maxime Bizon,
	Linux Kernel Mailing List, linux-mips-6z/3iImG2C8G8FEW9MqTrA,
	linux-watchdog-u79uwXL29TY76Z2rM5mHXA
  Cc: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
	Jonas Gorski

Add of_match_table for "brcm,bcm6345-wdt".

Use a NULL clock name when not on mach-bcm63xx so that the device tree
clock name does not have to be "periph".

Allow the watchdog to be selected on BMIPS_GENERIC and select the BCM6345
timer interrupt handler.

Signed-off-by: Simon Arlott <simon-A6De1vDTPLDsq35pWSNszA@public.gmane.org>
---
Patch 7 split into two patches.

On 25/11/15 02:44, Guenter Roeck wrote:
>> +	hw = devm_kzalloc(&pdev->dev, sizeof(*hw), GFP_KERNEL);
>> +	wdd = devm_kzalloc(&pdev->dev, sizeof(*wdd), GFP_KERNEL);
> 
> It would be better to allocate wdd as part of struct bcm63xx_wdt_hw.

This altered the context of this patch (was 10/10 now 11/11) because
platform_get_drvdata() is now "hw" instead of "wdd".

 drivers/watchdog/Kconfig       |  3 ++-
 drivers/watchdog/bcm63xx_wdt.c | 14 +++++++++++++-
 2 files changed, 15 insertions(+), 2 deletions(-)

diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index 6815b74..0c50add 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -1272,8 +1272,9 @@ config OCTEON_WDT
 
 config BCM63XX_WDT
 	tristate "Broadcom BCM63xx hardware watchdog"
-	depends on BCM63XX
+	depends on BCM63XX || BMIPS_GENERIC
 	select WATCHDOG_CORE
+	select BCM6345_L2_TIMER_IRQ if BMIPS_GENERIC
 	help
 	  Watchdog driver for the built in watchdog hardware in Broadcom
 	  BCM63xx SoC.
diff --git a/drivers/watchdog/bcm63xx_wdt.c b/drivers/watchdog/bcm63xx_wdt.c
index fa6c28b..4db4145 100644
--- a/drivers/watchdog/bcm63xx_wdt.c
+++ b/drivers/watchdog/bcm63xx_wdt.c
@@ -22,6 +22,7 @@
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/moduleparam.h>
+#include <linux/mod_devicetable.h>
 #include <linux/spinlock.h>
 #include <linux/types.h>
 #include <linux/watchdog.h>
@@ -31,7 +32,11 @@
 
 #define PFX KBUILD_MODNAME
 
-#define WDT_CLK_NAME		"periph"
+#ifdef CONFIG_BCM63XX
+# define WDT_CLK_NAME		"periph"
+#else
+# define WDT_CLK_NAME		NULL
+#endif
 
 struct bcm63xx_wdt_hw {
 	struct watchdog_device wdd;
@@ -286,12 +291,19 @@ static void bcm63xx_wdt_shutdown(struct platform_device *pdev)
 	bcm63xx_wdt_stop(&hw->wdd);
 }
 
+static const struct of_device_id bcm63xx_wdt_dt_ids[] = {
+	{ .compatible = "brcm,bcm6345-wdt" },
+	{}
+};
+MODULE_DEVICE_TABLE(of, bcm63xx_wdt_dt_ids);
+
 static struct platform_driver bcm63xx_wdt_driver = {
 	.probe	= bcm63xx_wdt_probe,
 	.remove = bcm63xx_wdt_remove,
 	.shutdown = bcm63xx_wdt_shutdown,
 	.driver = {
 		.name = "bcm63xx-wdt",
+		.of_match_table = bcm63xx_wdt_dt_ids,
 	}
 };
 
-- 
2.1.4

-- 
Simon Arlott
--
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] 95+ messages in thread

* [PATCH (v4) 2/11] MIPS: bmips: Add bcm6345-l2-timer interrupt controller
  2015-11-24 22:36           ` Florian Fainelli
  (?)
@ 2015-11-26 22:32           ` Simon Arlott
  2015-11-27  8:37               ` Thomas Gleixner
  -1 siblings, 1 reply; 95+ messages in thread
From: Simon Arlott @ 2015-11-26 22:32 UTC (permalink / raw)
  To: Florian Fainelli, MIPS Mailing List
  Cc: Jonas Gorski, devicetree, Ralf Baechle, Thomas Gleixner,
	Jason Cooper, Marc Zyngier, Kevin Cernekee, Wim Van Sebroeck,
	Miguel Gaio, Maxime Bizon, Linux Kernel Mailing List,
	linux-watchdog, Rob Herring, Pawel Moll, Mark Rutland,
	Ian Campbell, Kumar Gala

Add the BCM6345/BCM6318 timer as an interrupt controller so that it can be
used by the watchdog to warn that its timer will expire soon.

Support for clocksource/clockevents is not implemented as the timer
interrupt is not per CPU (except on the BCM6318) and the MIPS clock is
better. This could be added later if required without changing the device
tree binding.

Signed-off-by: Simon Arlott <simon@fire.lp0.eu>
---
On 24/11/15 22:36, Florian Fainelli wrote:
> On 24/11/15 14:10, Simon Arlott wrote:
>> +static inline u32 bcm6345_timer_read_int_status(struct bcm6345_timer *timer)
>> +{
>> +	if (timer->type == TIMER_TYPE_6318)
>> +		return __raw_readl(timer->base + REG_6318_IRQ_STATUS);
>> +	else
>> +		return __raw_readb(timer->base + REG_6345_IRQ_STATUS);
> 
> This is really error prone and does not scale to adding a third type of
> timer if we ever had to. How about using the same logic and construction
> as the following drivers: drivers/spi/spi-bcm63xx.c,
> drivers/bus/brcmstb_gisb.c?

I've changed it to use an array of registers, but I still need to check
if the interrupt register size is 8 or 32 bits.

 drivers/irqchip/Kconfig                |   5 +
 drivers/irqchip/Makefile               |   1 +
 drivers/irqchip/irq-bcm6345-l2-timer.c | 339 +++++++++++++++++++++++++++++++++
 3 files changed, 345 insertions(+)
 create mode 100644 drivers/irqchip/irq-bcm6345-l2-timer.c

diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
index d307bb3..21c3d9b 100644
--- a/drivers/irqchip/Kconfig
+++ b/drivers/irqchip/Kconfig
@@ -70,6 +70,11 @@ config BCM6345_L1_IRQ
 	select GENERIC_IRQ_CHIP
 	select IRQ_DOMAIN
 
+config BCM6345_L2_TIMER_IRQ
+	bool
+	select GENERIC_IRQ_CHIP
+	select IRQ_DOMAIN
+
 config BCM7038_L1_IRQ
 	bool
 	select GENERIC_IRQ_CHIP
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index ded59cf..2687dea 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -44,6 +44,7 @@ obj-$(CONFIG_XTENSA_MX)			+= irq-xtensa-mx.o
 obj-$(CONFIG_IRQ_CROSSBAR)		+= irq-crossbar.o
 obj-$(CONFIG_SOC_VF610)			+= irq-vf610-mscm-ir.o
 obj-$(CONFIG_BCM6345_L1_IRQ)		+= irq-bcm6345-l1.o
+obj-$(CONFIG_BCM6345_L2_TIMER_IRQ)	+= irq-bcm6345-l2-timer.o
 obj-$(CONFIG_BCM7038_L1_IRQ)		+= irq-bcm7038-l1.o
 obj-$(CONFIG_BCM7120_L2_IRQ)		+= irq-bcm7120-l2.o
 obj-$(CONFIG_BRCMSTB_L2_IRQ)		+= irq-brcmstb-l2.o
diff --git a/drivers/irqchip/irq-bcm6345-l2-timer.c b/drivers/irqchip/irq-bcm6345-l2-timer.c
new file mode 100644
index 0000000..800f1ac
--- /dev/null
+++ b/drivers/irqchip/irq-bcm6345-l2-timer.c
@@ -0,0 +1,339 @@
+/*
+ * Copyright 2015 Simon Arlott
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * Based on arch/mips/bcm63xx/timer.c:
+ * Copyright (C) 2008 Maxime Bizon <mbizon@freebox.fr>
+ *
+ * Registers for SoCs with 4 timers: BCM6345, BCM6328, BCM6362, BCM6816,
+ *                                   BCM68220,BCM63168, BCM63268
+ *   0x02: Interrupt enable (u8)
+ *   0x03: Interrupt status (u8)
+ *   0x04: Timer 0 control
+ *   0x08: Timer 1 control
+ *   0x0c: Timer 2 control
+ *   0x10: Timer 0 count
+ *   0x14: Timer 1 count
+ *   0x18: Timer 2 count
+ *   0x1c+: Watchdog registers
+ *
+ * Registers for SoCs with 5 timers: BCM6318
+ *   0x00: Interrupt enable (u32)
+ *   0x04: Interrupt status (u32)
+ *   0x08: Timer 0 control
+ *   0x0c: Timer 1 control
+ *   0x10: Timer 2 control
+ *   0x14: Timer 3 control
+ *   0x18: Timer 0 count
+ *   0x1c: Timer 1 count
+ *   0x20: Timer 2 count
+ *   0x24: Timer 3 count
+ *   0x28+: Watchdog registers
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/bitops.h>
+#include <linux/interrupt.h>
+#include <linux/irqreturn.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/of_platform.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/string.h>
+#include <linux/irqchip.h>
+#include <linux/irqchip/chained_irq.h>
+
+enum timer_regs {
+	/* Interrupt enable register:
+	 *   1 bit per timer (without the watchdog)
+	 */
+	TIMER_INT_ENABLE = 0,
+
+	/* Interrupt status register:
+	 *   1 bit per timer (plus the watchdog after the timers)
+	 *   Read for status
+	 *   Write bit to ack
+	 */
+	TIMER_INT_STATUS,
+
+	/* Per-timer control register */
+	TIMER_CONTROL,
+
+	/* Per-timer count register */
+	TIMER_COUNT,
+};
+
+#define WATCHDOG_INT_BIT(x)		(BIT((x)->nr_timers))
+
+#define CONTROL_COUNTDOWN_MASK		(0x3fffffff)
+#define CONTROL_RSTCNTCLR_MASK		(1 << 30)
+#define CONTROL_ENABLE_MASK		(1 << 31)
+
+#define COUNT_MASK			(0x3fffffff)
+
+#define NR_TIMERS_6318			4
+#define INTR_BITS_6318			32
+static const long timer_regs_6318[] = {
+	[TIMER_INT_ENABLE]		= 0x00,
+	[TIMER_INT_STATUS]		= 0x04,
+	[TIMER_CONTROL]			= 0x08,
+	[TIMER_COUNT]			= 0x18,
+};
+
+#define NR_TIMERS_6345			3
+#define INTR_BITS_6345			8
+static const long timer_regs_6345[] = {
+	[TIMER_INT_ENABLE]		= 0x02,
+	[TIMER_INT_STATUS]		= 0x03,
+	[TIMER_CONTROL]			= 0x04,
+	[TIMER_COUNT]			= 0x10,
+};
+
+struct bcm6345_timer {
+	raw_spinlock_t lock;
+	void __iomem *base;
+	unsigned int irq;
+	struct irq_domain *domain;
+
+	const long *regs;
+	int interrupt_bits;
+	unsigned int nr_interrupts;
+	unsigned int nr_timers;
+};
+
+static inline u32 bcm6345_timer_read_int_status(struct bcm6345_timer *timer)
+{
+	if (timer->interrupt_bits == 32)
+		return __raw_readl(timer->base + timer->regs[TIMER_INT_STATUS]);
+	else
+		return __raw_readb(timer->base + timer->regs[TIMER_INT_STATUS]);
+}
+
+static inline void bcm6345_timer_write_int_status(struct bcm6345_timer *timer,
+	u32 val)
+{
+	if (timer->interrupt_bits == 32)
+		__raw_writel(val, timer->base + timer->regs[TIMER_INT_STATUS]);
+	else
+		__raw_writeb(val, timer->base + timer->regs[TIMER_INT_STATUS]);
+}
+
+static inline u32 bcm6345_timer_read_int_enable(struct bcm6345_timer *timer)
+{
+	if (timer->interrupt_bits == 32)
+		return __raw_readl(timer->base + timer->regs[TIMER_INT_ENABLE]);
+	else
+		return __raw_readb(timer->base + timer->regs[TIMER_INT_ENABLE]);
+}
+
+static inline void bcm6345_timer_write_int_enable(struct bcm6345_timer *timer,
+	u32 val)
+{
+	if (timer->interrupt_bits == 32)
+		__raw_writel(val, timer->base + timer->regs[TIMER_INT_ENABLE]);
+	else
+		__raw_writeb(val, timer->base + timer->regs[TIMER_INT_ENABLE]);
+}
+
+static inline void bcm6345_timer_write_control(struct bcm6345_timer *timer,
+	unsigned int id, u32 val)
+{
+	if (id >= timer->nr_timers) {
+		WARN(1, "%s: %d >= %d", __func__, id, timer->nr_timers);
+		return;
+	}
+
+	__raw_writel(val, timer->base + timer->regs[TIMER_CONTROL] + id * 4);
+}
+
+static inline void bcm6345_timer_write_count(struct bcm6345_timer *timer,
+	unsigned int id, u32 val)
+{
+	if (id >= timer->nr_timers) {
+		WARN(1, "%s: %d >= %d", __func__, id, timer->nr_timers);
+		return;
+	}
+
+	__raw_writel(val, timer->base + timer->regs[TIMER_COUNT] + id * 4);
+}
+
+static inline void bcm6345_timer_stop(struct bcm6345_timer *timer, int id)
+{
+	bcm6345_timer_write_control(timer, id, 0);
+	bcm6345_timer_write_count(timer, id, 0);
+	bcm6345_timer_write_int_status(timer, BIT(id));
+}
+
+static void bcm6345_timer_interrupt(struct irq_desc *desc)
+{
+	struct bcm6345_timer *timer = irq_desc_get_handler_data(desc);
+	struct irq_chip *chip = irq_desc_get_chip(desc);
+	unsigned long pending;
+	irq_hw_number_t hwirq;
+	unsigned int irq;
+
+	chained_irq_enter(chip, desc);
+
+	pending = bcm6345_timer_read_int_status(timer);
+	pending &= bcm6345_timer_read_int_enable(timer) |
+			WATCHDOG_INT_BIT(timer); /* Watchdog can't be masked */
+
+	for_each_set_bit(hwirq, &pending, timer->nr_interrupts) {
+		irq = irq_linear_revmap(timer->domain, hwirq);
+		if (irq)
+			do_IRQ(irq);
+		else
+			spurious_interrupt();
+	}
+
+	chained_irq_exit(chip, desc);
+}
+
+static void bcm6345_timer_unmask(struct irq_data *d)
+{
+	struct bcm6345_timer *timer = irq_data_get_irq_chip_data(d);
+	unsigned long flags;
+	u8 val;
+
+	if (d->hwirq < timer->nr_timers) {
+		raw_spin_lock_irqsave(&timer->lock, flags);
+		val = bcm6345_timer_read_int_enable(timer);
+		val |= BIT(d->hwirq);
+		bcm6345_timer_write_int_enable(timer, val);
+		raw_spin_unlock_irqrestore(&timer->lock, flags);
+	}
+}
+
+static void bcm6345_timer_mask(struct irq_data *d)
+{
+	struct bcm6345_timer *timer = irq_data_get_irq_chip_data(d);
+	unsigned long flags;
+	u32 val;
+
+	if (d->hwirq < timer->nr_timers) {
+		raw_spin_lock_irqsave(&timer->lock, flags);
+		val = bcm6345_timer_read_int_enable(timer);
+		val &= ~BIT(d->hwirq);
+		bcm6345_timer_write_int_enable(timer, val);
+		raw_spin_unlock_irqrestore(&timer->lock, flags);
+	}
+}
+
+static void bcm6345_timer_eoi(struct irq_data *d)
+{
+	struct bcm6345_timer *timer = irq_data_get_irq_chip_data(d);
+
+	bcm6345_timer_write_int_status(timer, BIT(d->hwirq));
+}
+
+static struct irq_chip bcm6345_timer_chip = {
+	.name			= "bcm6345-timer",
+	.irq_mask		= bcm6345_timer_mask,
+	.irq_unmask		= bcm6345_timer_unmask,
+	.irq_eoi		= bcm6345_timer_eoi,
+};
+
+static int bcm6345_timer_map(struct irq_domain *d, unsigned int virq,
+			     irq_hw_number_t hwirq)
+{
+	struct bcm6345_timer *timer = d->host_data;
+
+	irq_set_chip_and_handler(virq, &bcm6345_timer_chip,
+		hwirq < timer->nr_timers ?
+			handle_fasteoi_irq : handle_simple_irq);
+	irq_set_chip_data(virq, timer);
+	return 0;
+}
+
+static const struct irq_domain_ops bcm6345_timer_domain_ops = {
+	.xlate			= irq_domain_xlate_onecell,
+	.map			= bcm6345_timer_map,
+};
+
+static int __init bcm63xx_timer_init(struct device_node *node,
+	const char *name, const long *regs, int interrupt_bits,
+	unsigned int nr_timers)
+{
+	struct bcm6345_timer *timer;
+	int ret, i;
+
+	timer = kzalloc(sizeof(*timer), GFP_KERNEL);
+	if (!timer)
+		return -ENOMEM;
+
+	raw_spin_lock_init(&timer->lock);
+	timer->regs = regs;
+	timer->interrupt_bits = interrupt_bits;
+	timer->nr_timers = nr_timers;
+	timer->nr_interrupts = nr_timers + 1;
+
+	timer->irq = irq_of_parse_and_map(node, 0);
+	if (!timer->irq) {
+		pr_err("unable to map parent IRQ\n");
+		ret = -EINVAL;
+		goto free_timer;
+	}
+
+	timer->base = of_iomap(node, 0);
+	if (!timer->base) {
+		pr_err("unable to map registers\n");
+		ret = -ENOMEM;
+		goto free_timer;
+	}
+
+	timer->domain = irq_domain_add_linear(node, timer->nr_interrupts,
+					&bcm6345_timer_domain_ops, timer);
+	if (!timer->domain) {
+		pr_err("unable to add IRQ domain");
+		ret = -ENOMEM;
+		goto unmap_io;
+	}
+
+	/* Mask all interrupts and stop all timers */
+	bcm6345_timer_write_int_enable(timer, 0);
+	for (i = 0; i < timer->nr_timers; i++)
+		bcm6345_timer_stop(timer, i);
+
+	irq_set_chained_handler_and_data(timer->irq,
+					bcm6345_timer_interrupt, timer);
+
+	pr_info("registered %s L2 (timer) intc at MMIO 0x%p (irq = %d, IRQs: %d)\n",
+			name, timer->base, timer->irq, timer->nr_interrupts);
+	return 0;
+
+unmap_io:
+	iounmap(timer->base);
+free_timer:
+	kfree(timer);
+	return ret;
+}
+
+static int __init bcm6318_timer_init(struct device_node *node,
+				      struct device_node *parent)
+{
+	return bcm63xx_timer_init(node,
+		"BCM6318", timer_regs_6318, INTR_BITS_6318, NR_TIMERS_6318);
+}
+
+static int __init bcm6345_timer_init(struct device_node *node,
+				      struct device_node *parent)
+{
+	return bcm63xx_timer_init(node,
+		"BCM6345", timer_regs_6345, INTR_BITS_6345, NR_TIMERS_6345);
+}
+
+IRQCHIP_DECLARE(bcm6318_l2_timer, "brcm,bcm6318-timer", bcm6318_timer_init);
+IRQCHIP_DECLARE(bcm6345_l2_timer, "brcm,bcm6345-timer", bcm6345_timer_init);
-- 
2.1.4

-- 
Simon Arlott

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

* Re: [PATCH (v4) 2/11] MIPS: bmips: Add bcm6345-l2-timer interrupt controller
@ 2015-11-27  8:37               ` Thomas Gleixner
  0 siblings, 0 replies; 95+ messages in thread
From: Thomas Gleixner @ 2015-11-27  8:37 UTC (permalink / raw)
  To: Simon Arlott
  Cc: Florian Fainelli, MIPS Mailing List, Jonas Gorski, devicetree,
	Ralf Baechle, Jason Cooper, Marc Zyngier, Kevin Cernekee,
	Wim Van Sebroeck, Miguel Gaio, Maxime Bizon,
	Linux Kernel Mailing List, linux-watchdog, Rob Herring,
	Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala

Simon,

On Thu, 26 Nov 2015, Simon Arlott wrote:
> +static inline u32 bcm6345_timer_read_int_status(struct bcm6345_timer *timer)
> +{
> +	if (timer->interrupt_bits == 32)
> +		return __raw_readl(timer->base + timer->regs[TIMER_INT_STATUS]);
> +	else
> +		return __raw_readb(timer->base + timer->regs[TIMER_INT_STATUS]);
> +}

Instead of having that pile of conditionals you could just define two
functions and have a function pointer in struct bcm6345_timer which
you initialize at init time.

> +static inline void bcm6345_timer_write_control(struct bcm6345_timer *timer,
> +	unsigned int id, u32 val)
> +{
> +	if (id >= timer->nr_timers) {
> +		WARN(1, "%s: %d >= %d", __func__, id, timer->nr_timers);

This is more than silly. You call that form the init function via:

	for (i = 0; i < timer->nr_timers; i++)

Hmm?

> +static void bcm6345_timer_unmask(struct irq_data *d)
> +{
> +	struct bcm6345_timer *timer = irq_data_get_irq_chip_data(d);
> +	unsigned long flags;
> +	u8 val;
> +
> +	if (d->hwirq < timer->nr_timers) {

Again. You can have two different interrupt chips without that
completely undocumented and non obvious conditional.

BTW, how are those simple interrupts masked at all?

> +		raw_spin_lock_irqsave(&timer->lock, flags);
> +		val = bcm6345_timer_read_int_enable(timer);
> +		val |= BIT(d->hwirq);
> +		bcm6345_timer_write_int_enable(timer, val);
> +		raw_spin_unlock_irqrestore(&timer->lock, flags);
> +	}
> +}

> +	raw_spin_lock_init(&timer->lock);
> +	timer->regs = regs;
> +	timer->interrupt_bits = interrupt_bits;
> +	timer->nr_timers = nr_timers;
> +	timer->nr_interrupts = nr_timers + 1;

What is that extra interrupt about? For the casual reader this looks
like a bug ... Comments exist for a reason.

Thanks,

	tglx

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

* Re: [PATCH (v4) 2/11] MIPS: bmips: Add bcm6345-l2-timer interrupt controller
@ 2015-11-27  8:37               ` Thomas Gleixner
  0 siblings, 0 replies; 95+ messages in thread
From: Thomas Gleixner @ 2015-11-27  8:37 UTC (permalink / raw)
  To: Simon Arlott
  Cc: Florian Fainelli, MIPS Mailing List, Jonas Gorski,
	devicetree-u79uwXL29TY76Z2rM5mHXA, Ralf Baechle, Jason Cooper,
	Marc Zyngier, Kevin Cernekee, Wim Van Sebroeck, Miguel Gaio,
	Maxime Bizon, Linux Kernel Mailing List,
	linux-watchdog-u79uwXL29TY76Z2rM5mHXA, Rob Herring, Pawel Moll,
	Mark Rutland, Ian Campbell, Kumar Gala

Simon,

On Thu, 26 Nov 2015, Simon Arlott wrote:
> +static inline u32 bcm6345_timer_read_int_status(struct bcm6345_timer *timer)
> +{
> +	if (timer->interrupt_bits == 32)
> +		return __raw_readl(timer->base + timer->regs[TIMER_INT_STATUS]);
> +	else
> +		return __raw_readb(timer->base + timer->regs[TIMER_INT_STATUS]);
> +}

Instead of having that pile of conditionals you could just define two
functions and have a function pointer in struct bcm6345_timer which
you initialize at init time.

> +static inline void bcm6345_timer_write_control(struct bcm6345_timer *timer,
> +	unsigned int id, u32 val)
> +{
> +	if (id >= timer->nr_timers) {
> +		WARN(1, "%s: %d >= %d", __func__, id, timer->nr_timers);

This is more than silly. You call that form the init function via:

	for (i = 0; i < timer->nr_timers; i++)

Hmm?

> +static void bcm6345_timer_unmask(struct irq_data *d)
> +{
> +	struct bcm6345_timer *timer = irq_data_get_irq_chip_data(d);
> +	unsigned long flags;
> +	u8 val;
> +
> +	if (d->hwirq < timer->nr_timers) {

Again. You can have two different interrupt chips without that
completely undocumented and non obvious conditional.

BTW, how are those simple interrupts masked at all?

> +		raw_spin_lock_irqsave(&timer->lock, flags);
> +		val = bcm6345_timer_read_int_enable(timer);
> +		val |= BIT(d->hwirq);
> +		bcm6345_timer_write_int_enable(timer, val);
> +		raw_spin_unlock_irqrestore(&timer->lock, flags);
> +	}
> +}

> +	raw_spin_lock_init(&timer->lock);
> +	timer->regs = regs;
> +	timer->interrupt_bits = interrupt_bits;
> +	timer->nr_timers = nr_timers;
> +	timer->nr_interrupts = nr_timers + 1;

What is that extra interrupt about? For the casual reader this looks
like a bug ... Comments exist for a reason.

Thanks,

	tglx
--
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] 95+ messages in thread

* [PATCH (v5) 3/11] MIPS: bmips: Add bcm6345-l2-timer interrupt controller
  2015-11-27  8:37               ` Thomas Gleixner
@ 2015-11-28 12:26                 ` Simon Arlott
  -1 siblings, 0 replies; 95+ messages in thread
From: Simon Arlott @ 2015-11-28 12:26 UTC (permalink / raw)
  To: Thomas Gleixner
  Cc: Florian Fainelli, MIPS Mailing List, Jonas Gorski, devicetree,
	Ralf Baechle, Jason Cooper, Marc Zyngier, Kevin Cernekee,
	Wim Van Sebroeck, Miguel Gaio, Maxime Bizon,
	Linux Kernel Mailing List, linux-watchdog, Rob Herring,
	Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala

Add the BCM6345/BCM6318 timer as an interrupt controller so that it can be
used by the watchdog to warn that its timer will expire soon.

Support for clocksource/clockevents is not implemented as the timer
interrupt is not per CPU (except on the BCM6318) and the MIPS clock is
better. This could be added later if required without changing the device
tree binding.

Signed-off-by: Simon Arlott <simon@fire.lp0.eu>
---
On 27/11/15 08:37, Thomas Gleixner wrote:
> Instead of having that pile of conditionals you could just define two
> functions and have a function pointer in struct bcm6345_timer which
> you initialize at init time.

Fixed.

>> +static inline void bcm6345_timer_write_control(struct bcm6345_timer *timer,
>> + unsigned int id, u32 val)
>> +{
>> + if (id >= timer->nr_timers) {
>> + WARN(1, "%s: %d >= %d", __func__, id, timer->nr_timers);
>
> Hmm?

I've now removed this.

>> +static void bcm6345_timer_unmask(struct irq_data *d)
>> +{
>> + struct bcm6345_timer *timer = irq_data_get_irq_chip_data(d);
>> + unsigned long flags;
>> + u8 val;
>> +
>> + if (d->hwirq < timer->nr_timers) {
>
> Again. You can have two different interrupt chips without that
> completely undocumented and non obvious conditional.

Fixed.

> BTW, how are those simple interrupts masked at all?

The interrupt for the watchdog can't be masked. I've now used a noop
function for irq_enable/irq_disable.

>> + timer->nr_timers = nr_timers;
>> + timer->nr_interrupts = nr_timers + 1;
>
> What is that extra interrupt about? For the casual reader this looks
> like a bug ... Comments exist for a reason.

Fixed.

 drivers/irqchip/Kconfig                |   5 +
 drivers/irqchip/Makefile               |   1 +
 drivers/irqchip/irq-bcm6345-l2-timer.c | 386 +++++++++++++++++++++++++++++++++
 3 files changed, 392 insertions(+)
 create mode 100644 drivers/irqchip/irq-bcm6345-l2-timer.c

diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
index d307bb3..21c3d9b 100644
--- a/drivers/irqchip/Kconfig
+++ b/drivers/irqchip/Kconfig
@@ -70,6 +70,11 @@ config BCM6345_L1_IRQ
 	select GENERIC_IRQ_CHIP
 	select IRQ_DOMAIN
 
+config BCM6345_L2_TIMER_IRQ
+	bool
+	select GENERIC_IRQ_CHIP
+	select IRQ_DOMAIN
+
 config BCM7038_L1_IRQ
 	bool
 	select GENERIC_IRQ_CHIP
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index ded59cf..2687dea 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -44,6 +44,7 @@ obj-$(CONFIG_XTENSA_MX)			+= irq-xtensa-mx.o
 obj-$(CONFIG_IRQ_CROSSBAR)		+= irq-crossbar.o
 obj-$(CONFIG_SOC_VF610)			+= irq-vf610-mscm-ir.o
 obj-$(CONFIG_BCM6345_L1_IRQ)		+= irq-bcm6345-l1.o
+obj-$(CONFIG_BCM6345_L2_TIMER_IRQ)	+= irq-bcm6345-l2-timer.o
 obj-$(CONFIG_BCM7038_L1_IRQ)		+= irq-bcm7038-l1.o
 obj-$(CONFIG_BCM7120_L2_IRQ)		+= irq-bcm7120-l2.o
 obj-$(CONFIG_BRCMSTB_L2_IRQ)		+= irq-brcmstb-l2.o
diff --git a/drivers/irqchip/irq-bcm6345-l2-timer.c b/drivers/irqchip/irq-bcm6345-l2-timer.c
new file mode 100644
index 0000000..f3acda7
--- /dev/null
+++ b/drivers/irqchip/irq-bcm6345-l2-timer.c
@@ -0,0 +1,386 @@
+/*
+ * Copyright 2015 Simon Arlott
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * Based on arch/mips/bcm63xx/timer.c:
+ * Copyright (C) 2008 Maxime Bizon <mbizon@freebox.fr>
+ *
+ * Registers for SoCs with 4 timers: BCM6345, BCM6328, BCM6362, BCM6816,
+ *                                   BCM68220,BCM63168, BCM63268
+ *   0x02: Interrupt enable (u8)
+ *   0x03: Interrupt status (u8)
+ *   0x04: Timer 0 control
+ *   0x08: Timer 1 control
+ *   0x0c: Timer 2 control
+ *   0x10: Timer 0 count
+ *   0x14: Timer 1 count
+ *   0x18: Timer 2 count
+ *   0x1c+: Watchdog registers
+ *
+ * Registers for SoCs with 5 timers: BCM6318
+ *   0x00: Interrupt enable (u32)
+ *   0x04: Interrupt status (u32)
+ *   0x08: Timer 0 control
+ *   0x0c: Timer 1 control
+ *   0x10: Timer 2 control
+ *   0x14: Timer 3 control
+ *   0x18: Timer 0 count
+ *   0x1c: Timer 1 count
+ *   0x20: Timer 2 count
+ *   0x24: Timer 3 count
+ *   0x28+: Watchdog registers
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/bitops.h>
+#include <linux/interrupt.h>
+#include <linux/irqreturn.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/of_platform.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/string.h>
+#include <linux/irqchip.h>
+#include <linux/irqchip/chained_irq.h>
+
+enum timer_regs {
+	/* Interrupt enable register:
+	 *   1 bit per timer (without the watchdog)
+	 */
+	TIMER_INT_ENABLE = 0,
+
+	/* Interrupt status register:
+	 *   1 bit per timer (plus the watchdog)
+	 *   Read for status
+	 *   Write bit to ack
+	 */
+	TIMER_INT_STATUS,
+
+	/* Per-timer control register */
+	TIMER_CONTROL,
+
+	/* Per-timer count register */
+	TIMER_COUNT,
+
+
+	/* Number of registers in enum */
+	__TIMER_REGS_ENUM_SIZE
+};
+
+/* Watchdog interrupt is immediately after the timers */
+#define WATCHDOG_INT_BIT(x)		(BIT((x)->variant.nr_timers))
+
+#define CONTROL_COUNTDOWN_MASK		(0x3fffffff)
+#define CONTROL_RSTCNTCLR_MASK		(1 << 30)
+#define CONTROL_ENABLE_MASK		(1 << 31)
+
+#define COUNT_MASK			(0x3fffffff)
+
+struct bcm6345_timer *timer;
+
+struct bcm6345_timer_variant {
+	unsigned int nr_timers;
+	u32 (*int_read)(struct bcm6345_timer *timer, int reg);
+	void (*int_write)(struct bcm6345_timer *timer, int reg, u32 val);
+	long regs[__TIMER_REGS_ENUM_SIZE];
+};
+
+struct bcm6345_timer {
+	raw_spinlock_t lock;
+	void __iomem *base;
+	unsigned int irq;
+	struct irq_domain *domain;
+
+	struct bcm6345_timer_variant variant;
+	unsigned int nr_interrupts;
+};
+
+
+/* Interrupt enable/status are either 8-bit or 32-bit registers */
+
+static u32 bcm6345_timer_int_readl(struct bcm6345_timer *timer, int reg)
+{
+	return __raw_readl(timer->base + timer->variant.regs[reg]);
+}
+
+static void bcm6345_timer_int_writel(struct bcm6345_timer *timer,
+	int reg, u32 val)
+{
+	__raw_writel(val, timer->base + timer->variant.regs[reg]);
+}
+
+static u32 bcm6345_timer_int_readb(struct bcm6345_timer *timer, int reg)
+{
+	return __raw_readb(timer->base + timer->variant.regs[reg]);
+}
+
+static void bcm6345_timer_int_writeb(struct bcm6345_timer *timer,
+	int reg, u32 val)
+{
+	__raw_writeb(val, timer->base + timer->variant.regs[reg]);
+}
+
+
+/* Timer variants */
+
+static const struct bcm6345_timer_variant timer_bcm6318 __initconst = {
+	.nr_timers = 4,
+	.regs = {
+		[TIMER_INT_ENABLE]	= 0x00,
+		[TIMER_INT_STATUS]	= 0x04,
+		[TIMER_CONTROL]		= 0x08,
+		[TIMER_COUNT]		= 0x18,
+	},
+	.int_read = bcm6345_timer_int_readl,
+	.int_write = bcm6345_timer_int_writel,
+};
+
+static const struct bcm6345_timer_variant timer_bcm6345 __initconst = {
+	.nr_timers = 3,
+	.regs = {
+		[TIMER_INT_ENABLE]	= 0x02,
+		[TIMER_INT_STATUS]	= 0x03,
+		[TIMER_CONTROL]		= 0x04,
+		[TIMER_COUNT]		= 0x10,
+	},
+	.int_read = bcm6345_timer_int_readb,
+	.int_write = bcm6345_timer_int_writeb,
+};
+
+
+/* Register access functions */
+
+static inline u32 bcm6345_timer_read_int_status(struct bcm6345_timer *timer)
+{
+	return timer->variant.int_read(timer, TIMER_INT_STATUS);
+}
+
+static inline void bcm6345_timer_write_int_status(struct bcm6345_timer *timer,
+	u32 val)
+{
+	timer->variant.int_write(timer, TIMER_INT_STATUS, val);
+}
+
+static inline u32 bcm6345_timer_read_int_enable(struct bcm6345_timer *timer)
+{
+	return timer->variant.int_read(timer, TIMER_INT_ENABLE);
+}
+
+static inline void bcm6345_timer_write_int_enable(struct bcm6345_timer *timer,
+	u32 val)
+{
+	timer->variant.int_write(timer, TIMER_INT_ENABLE, val);
+}
+
+static inline __init void bcm6345_timer_write_control(
+	struct bcm6345_timer *timer, unsigned int id, u32 val)
+{
+	__raw_writel(val,
+		timer->base + timer->variant.regs[TIMER_CONTROL] + id * 4);
+}
+
+static inline __init void bcm6345_timer_write_count(
+	struct bcm6345_timer *timer, unsigned int id, u32 val)
+{
+	__raw_writel(val,
+		timer->base + timer->variant.regs[TIMER_COUNT] + id * 4);
+}
+
+static inline __init void bcm6345_timer_stop(struct bcm6345_timer *timer,
+	unsigned int id)
+{
+	bcm6345_timer_write_control(timer, id, 0);
+	bcm6345_timer_write_count(timer, id, 0);
+	bcm6345_timer_write_int_status(timer, BIT(id));
+}
+
+
+/* Interrupt handler functions */
+
+static void bcm6345_timer_interrupt(struct irq_desc *desc)
+{
+	struct bcm6345_timer *timer = irq_desc_get_handler_data(desc);
+	struct irq_chip *chip = irq_desc_get_chip(desc);
+	unsigned long pending;
+	irq_hw_number_t hwirq;
+	unsigned int irq;
+
+	chained_irq_enter(chip, desc);
+
+	pending = bcm6345_timer_read_int_status(timer);
+	pending &= bcm6345_timer_read_int_enable(timer) |
+			WATCHDOG_INT_BIT(timer); /* Watchdog can't be masked */
+
+	for_each_set_bit(hwirq, &pending, timer->nr_interrupts) {
+		irq = irq_linear_revmap(timer->domain, hwirq);
+		if (irq)
+			do_IRQ(irq);
+		else
+			spurious_interrupt();
+	}
+
+	chained_irq_exit(chip, desc);
+}
+
+static void bcm6345_timer_enable(struct irq_data *d)
+{
+	struct bcm6345_timer *timer = irq_data_get_irq_chip_data(d);
+	unsigned long flags;
+	u8 val;
+
+	raw_spin_lock_irqsave(&timer->lock, flags);
+	val = bcm6345_timer_read_int_enable(timer);
+	val |= BIT(d->hwirq);
+	bcm6345_timer_write_int_enable(timer, val);
+	raw_spin_unlock_irqrestore(&timer->lock, flags);
+}
+
+static void bcm6345_timer_disable(struct irq_data *d)
+{
+	struct bcm6345_timer *timer = irq_data_get_irq_chip_data(d);
+	unsigned long flags;
+	u32 val;
+
+	raw_spin_lock_irqsave(&timer->lock, flags);
+	val = bcm6345_timer_read_int_enable(timer);
+	val &= ~BIT(d->hwirq);
+	bcm6345_timer_write_int_enable(timer, val);
+	raw_spin_unlock_irqrestore(&timer->lock, flags);
+}
+
+static void bcm6345_timer_eoi(struct irq_data *d)
+{
+	struct bcm6345_timer *timer = irq_data_get_irq_chip_data(d);
+
+	bcm6345_timer_write_int_status(timer, BIT(d->hwirq));
+}
+
+static struct irq_chip bcm6345_timer_chip = {
+	.name		= "bcm6345-timer",
+	.irq_enable	= bcm6345_timer_enable,
+	.irq_disable	= bcm6345_timer_disable,
+	.irq_eoi	= bcm6345_timer_eoi,
+};
+
+static void bcm6345_timer_irq_noop(struct irq_data *d)
+{
+	/* The watchdog interrupt can't be masked (its
+	 * enable bit has no effect), so do nothing.
+	 */
+}
+
+static struct irq_chip bcm6345_timer_wdt_chip = {
+	.name		= "bcm6345-timer",
+	.irq_enable	= bcm6345_timer_irq_noop,
+	.irq_disable	= bcm6345_timer_irq_noop,
+};
+
+static int bcm6345_timer_map(struct irq_domain *d, unsigned int virq,
+			     irq_hw_number_t hwirq)
+{
+	struct bcm6345_timer *timer = d->host_data;
+
+	if (hwirq < timer->variant.nr_timers) {
+		irq_set_chip_and_handler(virq, &bcm6345_timer_chip,
+			handle_fasteoi_irq);
+	} else {
+		/* Watchdog interrupt can't be disabled or acked */
+		irq_set_chip_and_handler(virq, &bcm6345_timer_wdt_chip,
+			handle_simple_irq);
+	}
+	irq_set_chip_data(virq, timer);
+	return 0;
+}
+
+static const struct irq_domain_ops bcm6345_timer_domain_ops = {
+	.xlate	= irq_domain_xlate_onecell,
+	.map	= bcm6345_timer_map,
+};
+
+static int __init bcm63xx_timer_init(struct device_node *node,
+	const char *name, const struct bcm6345_timer_variant *variant)
+{
+	struct bcm6345_timer *timer;
+	unsigned int i;
+	int ret;
+
+	timer = kzalloc(sizeof(*timer), GFP_KERNEL);
+	if (!timer)
+		return -ENOMEM;
+
+	raw_spin_lock_init(&timer->lock);
+	memcpy(&timer->variant, variant, sizeof(*variant));
+	/* The watchdog warning event is the next interrupt bit
+	 * after the timers. It has different control/countdown
+	 * registers, handled by the watchdog driver.
+	 */
+	timer->nr_interrupts = timer->variant.nr_timers + 1;
+
+	timer->irq = irq_of_parse_and_map(node, 0);
+	if (!timer->irq) {
+		pr_err("unable to map parent IRQ\n");
+		ret = -EINVAL;
+		goto free_timer;
+	}
+
+	timer->base = of_iomap(node, 0);
+	if (!timer->base) {
+		pr_err("unable to map registers\n");
+		ret = -ENOMEM;
+		goto free_timer;
+	}
+
+	timer->domain = irq_domain_add_linear(node, timer->nr_interrupts,
+					&bcm6345_timer_domain_ops, timer);
+	if (!timer->domain) {
+		pr_err("unable to add IRQ domain");
+		ret = -ENOMEM;
+		goto unmap_io;
+	}
+
+	/* Mask all interrupts and stop all timers */
+	bcm6345_timer_write_int_enable(timer, 0);
+	for (i = 0; i < timer->variant.nr_timers; i++)
+		bcm6345_timer_stop(timer, i);
+
+	irq_set_chained_handler_and_data(timer->irq,
+					bcm6345_timer_interrupt, timer);
+
+	pr_info("registered %s L2 (timer) intc at MMIO 0x%p (irq = %d, IRQs: %d)\n",
+			name, timer->base, timer->irq, timer->nr_interrupts);
+	return 0;
+
+unmap_io:
+	iounmap(timer->base);
+free_timer:
+	kfree(timer);
+	return ret;
+}
+
+static int __init bcm6318_timer_init(struct device_node *node,
+				      struct device_node *parent)
+{
+	return bcm63xx_timer_init(node, "BCM6318", &timer_bcm6318);
+}
+
+static int __init bcm6345_timer_init(struct device_node *node,
+				      struct device_node *parent)
+{
+	return bcm63xx_timer_init(node, "BCM6345", &timer_bcm6345);
+}
+
+IRQCHIP_DECLARE(bcm6318_l2_timer, "brcm,bcm6318-timer", bcm6318_timer_init);
+IRQCHIP_DECLARE(bcm6345_l2_timer, "brcm,bcm6345-timer", bcm6345_timer_init);
-- 
2.1.4

-- 
Simon Arlott

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

* [PATCH (v5) 3/11] MIPS: bmips: Add bcm6345-l2-timer interrupt controller
@ 2015-11-28 12:26                 ` Simon Arlott
  0 siblings, 0 replies; 95+ messages in thread
From: Simon Arlott @ 2015-11-28 12:26 UTC (permalink / raw)
  To: Thomas Gleixner
  Cc: Florian Fainelli, MIPS Mailing List, Jonas Gorski,
	devicetree-u79uwXL29TY76Z2rM5mHXA, Ralf Baechle, Jason Cooper,
	Marc Zyngier, Kevin Cernekee, Wim Van Sebroeck, Miguel Gaio,
	Maxime Bizon, Linux Kernel Mailing List,
	linux-watchdog-u79uwXL29TY76Z2rM5mHXA, Rob Herring, Pawel Moll,
	Mark Rutland, Ian Campbell, Kumar Gala

Add the BCM6345/BCM6318 timer as an interrupt controller so that it can be
used by the watchdog to warn that its timer will expire soon.

Support for clocksource/clockevents is not implemented as the timer
interrupt is not per CPU (except on the BCM6318) and the MIPS clock is
better. This could be added later if required without changing the device
tree binding.

Signed-off-by: Simon Arlott <simon-A6De1vDTPLDsq35pWSNszA@public.gmane.org>
---
On 27/11/15 08:37, Thomas Gleixner wrote:
> Instead of having that pile of conditionals you could just define two
> functions and have a function pointer in struct bcm6345_timer which
> you initialize at init time.

Fixed.

>> +static inline void bcm6345_timer_write_control(struct bcm6345_timer *timer,
>> + unsigned int id, u32 val)
>> +{
>> + if (id >= timer->nr_timers) {
>> + WARN(1, "%s: %d >= %d", __func__, id, timer->nr_timers);
>
> Hmm?

I've now removed this.

>> +static void bcm6345_timer_unmask(struct irq_data *d)
>> +{
>> + struct bcm6345_timer *timer = irq_data_get_irq_chip_data(d);
>> + unsigned long flags;
>> + u8 val;
>> +
>> + if (d->hwirq < timer->nr_timers) {
>
> Again. You can have two different interrupt chips without that
> completely undocumented and non obvious conditional.

Fixed.

> BTW, how are those simple interrupts masked at all?

The interrupt for the watchdog can't be masked. I've now used a noop
function for irq_enable/irq_disable.

>> + timer->nr_timers = nr_timers;
>> + timer->nr_interrupts = nr_timers + 1;
>
> What is that extra interrupt about? For the casual reader this looks
> like a bug ... Comments exist for a reason.

Fixed.

 drivers/irqchip/Kconfig                |   5 +
 drivers/irqchip/Makefile               |   1 +
 drivers/irqchip/irq-bcm6345-l2-timer.c | 386 +++++++++++++++++++++++++++++++++
 3 files changed, 392 insertions(+)
 create mode 100644 drivers/irqchip/irq-bcm6345-l2-timer.c

diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
index d307bb3..21c3d9b 100644
--- a/drivers/irqchip/Kconfig
+++ b/drivers/irqchip/Kconfig
@@ -70,6 +70,11 @@ config BCM6345_L1_IRQ
 	select GENERIC_IRQ_CHIP
 	select IRQ_DOMAIN
 
+config BCM6345_L2_TIMER_IRQ
+	bool
+	select GENERIC_IRQ_CHIP
+	select IRQ_DOMAIN
+
 config BCM7038_L1_IRQ
 	bool
 	select GENERIC_IRQ_CHIP
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index ded59cf..2687dea 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -44,6 +44,7 @@ obj-$(CONFIG_XTENSA_MX)			+= irq-xtensa-mx.o
 obj-$(CONFIG_IRQ_CROSSBAR)		+= irq-crossbar.o
 obj-$(CONFIG_SOC_VF610)			+= irq-vf610-mscm-ir.o
 obj-$(CONFIG_BCM6345_L1_IRQ)		+= irq-bcm6345-l1.o
+obj-$(CONFIG_BCM6345_L2_TIMER_IRQ)	+= irq-bcm6345-l2-timer.o
 obj-$(CONFIG_BCM7038_L1_IRQ)		+= irq-bcm7038-l1.o
 obj-$(CONFIG_BCM7120_L2_IRQ)		+= irq-bcm7120-l2.o
 obj-$(CONFIG_BRCMSTB_L2_IRQ)		+= irq-brcmstb-l2.o
diff --git a/drivers/irqchip/irq-bcm6345-l2-timer.c b/drivers/irqchip/irq-bcm6345-l2-timer.c
new file mode 100644
index 0000000..f3acda7
--- /dev/null
+++ b/drivers/irqchip/irq-bcm6345-l2-timer.c
@@ -0,0 +1,386 @@
+/*
+ * Copyright 2015 Simon Arlott
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * Based on arch/mips/bcm63xx/timer.c:
+ * Copyright (C) 2008 Maxime Bizon <mbizon-MmRyKUhfbQ9GWvitb5QawA@public.gmane.org>
+ *
+ * Registers for SoCs with 4 timers: BCM6345, BCM6328, BCM6362, BCM6816,
+ *                                   BCM68220,BCM63168, BCM63268
+ *   0x02: Interrupt enable (u8)
+ *   0x03: Interrupt status (u8)
+ *   0x04: Timer 0 control
+ *   0x08: Timer 1 control
+ *   0x0c: Timer 2 control
+ *   0x10: Timer 0 count
+ *   0x14: Timer 1 count
+ *   0x18: Timer 2 count
+ *   0x1c+: Watchdog registers
+ *
+ * Registers for SoCs with 5 timers: BCM6318
+ *   0x00: Interrupt enable (u32)
+ *   0x04: Interrupt status (u32)
+ *   0x08: Timer 0 control
+ *   0x0c: Timer 1 control
+ *   0x10: Timer 2 control
+ *   0x14: Timer 3 control
+ *   0x18: Timer 0 count
+ *   0x1c: Timer 1 count
+ *   0x20: Timer 2 count
+ *   0x24: Timer 3 count
+ *   0x28+: Watchdog registers
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/bitops.h>
+#include <linux/interrupt.h>
+#include <linux/irqreturn.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/of_platform.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/string.h>
+#include <linux/irqchip.h>
+#include <linux/irqchip/chained_irq.h>
+
+enum timer_regs {
+	/* Interrupt enable register:
+	 *   1 bit per timer (without the watchdog)
+	 */
+	TIMER_INT_ENABLE = 0,
+
+	/* Interrupt status register:
+	 *   1 bit per timer (plus the watchdog)
+	 *   Read for status
+	 *   Write bit to ack
+	 */
+	TIMER_INT_STATUS,
+
+	/* Per-timer control register */
+	TIMER_CONTROL,
+
+	/* Per-timer count register */
+	TIMER_COUNT,
+
+
+	/* Number of registers in enum */
+	__TIMER_REGS_ENUM_SIZE
+};
+
+/* Watchdog interrupt is immediately after the timers */
+#define WATCHDOG_INT_BIT(x)		(BIT((x)->variant.nr_timers))
+
+#define CONTROL_COUNTDOWN_MASK		(0x3fffffff)
+#define CONTROL_RSTCNTCLR_MASK		(1 << 30)
+#define CONTROL_ENABLE_MASK		(1 << 31)
+
+#define COUNT_MASK			(0x3fffffff)
+
+struct bcm6345_timer *timer;
+
+struct bcm6345_timer_variant {
+	unsigned int nr_timers;
+	u32 (*int_read)(struct bcm6345_timer *timer, int reg);
+	void (*int_write)(struct bcm6345_timer *timer, int reg, u32 val);
+	long regs[__TIMER_REGS_ENUM_SIZE];
+};
+
+struct bcm6345_timer {
+	raw_spinlock_t lock;
+	void __iomem *base;
+	unsigned int irq;
+	struct irq_domain *domain;
+
+	struct bcm6345_timer_variant variant;
+	unsigned int nr_interrupts;
+};
+
+
+/* Interrupt enable/status are either 8-bit or 32-bit registers */
+
+static u32 bcm6345_timer_int_readl(struct bcm6345_timer *timer, int reg)
+{
+	return __raw_readl(timer->base + timer->variant.regs[reg]);
+}
+
+static void bcm6345_timer_int_writel(struct bcm6345_timer *timer,
+	int reg, u32 val)
+{
+	__raw_writel(val, timer->base + timer->variant.regs[reg]);
+}
+
+static u32 bcm6345_timer_int_readb(struct bcm6345_timer *timer, int reg)
+{
+	return __raw_readb(timer->base + timer->variant.regs[reg]);
+}
+
+static void bcm6345_timer_int_writeb(struct bcm6345_timer *timer,
+	int reg, u32 val)
+{
+	__raw_writeb(val, timer->base + timer->variant.regs[reg]);
+}
+
+
+/* Timer variants */
+
+static const struct bcm6345_timer_variant timer_bcm6318 __initconst = {
+	.nr_timers = 4,
+	.regs = {
+		[TIMER_INT_ENABLE]	= 0x00,
+		[TIMER_INT_STATUS]	= 0x04,
+		[TIMER_CONTROL]		= 0x08,
+		[TIMER_COUNT]		= 0x18,
+	},
+	.int_read = bcm6345_timer_int_readl,
+	.int_write = bcm6345_timer_int_writel,
+};
+
+static const struct bcm6345_timer_variant timer_bcm6345 __initconst = {
+	.nr_timers = 3,
+	.regs = {
+		[TIMER_INT_ENABLE]	= 0x02,
+		[TIMER_INT_STATUS]	= 0x03,
+		[TIMER_CONTROL]		= 0x04,
+		[TIMER_COUNT]		= 0x10,
+	},
+	.int_read = bcm6345_timer_int_readb,
+	.int_write = bcm6345_timer_int_writeb,
+};
+
+
+/* Register access functions */
+
+static inline u32 bcm6345_timer_read_int_status(struct bcm6345_timer *timer)
+{
+	return timer->variant.int_read(timer, TIMER_INT_STATUS);
+}
+
+static inline void bcm6345_timer_write_int_status(struct bcm6345_timer *timer,
+	u32 val)
+{
+	timer->variant.int_write(timer, TIMER_INT_STATUS, val);
+}
+
+static inline u32 bcm6345_timer_read_int_enable(struct bcm6345_timer *timer)
+{
+	return timer->variant.int_read(timer, TIMER_INT_ENABLE);
+}
+
+static inline void bcm6345_timer_write_int_enable(struct bcm6345_timer *timer,
+	u32 val)
+{
+	timer->variant.int_write(timer, TIMER_INT_ENABLE, val);
+}
+
+static inline __init void bcm6345_timer_write_control(
+	struct bcm6345_timer *timer, unsigned int id, u32 val)
+{
+	__raw_writel(val,
+		timer->base + timer->variant.regs[TIMER_CONTROL] + id * 4);
+}
+
+static inline __init void bcm6345_timer_write_count(
+	struct bcm6345_timer *timer, unsigned int id, u32 val)
+{
+	__raw_writel(val,
+		timer->base + timer->variant.regs[TIMER_COUNT] + id * 4);
+}
+
+static inline __init void bcm6345_timer_stop(struct bcm6345_timer *timer,
+	unsigned int id)
+{
+	bcm6345_timer_write_control(timer, id, 0);
+	bcm6345_timer_write_count(timer, id, 0);
+	bcm6345_timer_write_int_status(timer, BIT(id));
+}
+
+
+/* Interrupt handler functions */
+
+static void bcm6345_timer_interrupt(struct irq_desc *desc)
+{
+	struct bcm6345_timer *timer = irq_desc_get_handler_data(desc);
+	struct irq_chip *chip = irq_desc_get_chip(desc);
+	unsigned long pending;
+	irq_hw_number_t hwirq;
+	unsigned int irq;
+
+	chained_irq_enter(chip, desc);
+
+	pending = bcm6345_timer_read_int_status(timer);
+	pending &= bcm6345_timer_read_int_enable(timer) |
+			WATCHDOG_INT_BIT(timer); /* Watchdog can't be masked */
+
+	for_each_set_bit(hwirq, &pending, timer->nr_interrupts) {
+		irq = irq_linear_revmap(timer->domain, hwirq);
+		if (irq)
+			do_IRQ(irq);
+		else
+			spurious_interrupt();
+	}
+
+	chained_irq_exit(chip, desc);
+}
+
+static void bcm6345_timer_enable(struct irq_data *d)
+{
+	struct bcm6345_timer *timer = irq_data_get_irq_chip_data(d);
+	unsigned long flags;
+	u8 val;
+
+	raw_spin_lock_irqsave(&timer->lock, flags);
+	val = bcm6345_timer_read_int_enable(timer);
+	val |= BIT(d->hwirq);
+	bcm6345_timer_write_int_enable(timer, val);
+	raw_spin_unlock_irqrestore(&timer->lock, flags);
+}
+
+static void bcm6345_timer_disable(struct irq_data *d)
+{
+	struct bcm6345_timer *timer = irq_data_get_irq_chip_data(d);
+	unsigned long flags;
+	u32 val;
+
+	raw_spin_lock_irqsave(&timer->lock, flags);
+	val = bcm6345_timer_read_int_enable(timer);
+	val &= ~BIT(d->hwirq);
+	bcm6345_timer_write_int_enable(timer, val);
+	raw_spin_unlock_irqrestore(&timer->lock, flags);
+}
+
+static void bcm6345_timer_eoi(struct irq_data *d)
+{
+	struct bcm6345_timer *timer = irq_data_get_irq_chip_data(d);
+
+	bcm6345_timer_write_int_status(timer, BIT(d->hwirq));
+}
+
+static struct irq_chip bcm6345_timer_chip = {
+	.name		= "bcm6345-timer",
+	.irq_enable	= bcm6345_timer_enable,
+	.irq_disable	= bcm6345_timer_disable,
+	.irq_eoi	= bcm6345_timer_eoi,
+};
+
+static void bcm6345_timer_irq_noop(struct irq_data *d)
+{
+	/* The watchdog interrupt can't be masked (its
+	 * enable bit has no effect), so do nothing.
+	 */
+}
+
+static struct irq_chip bcm6345_timer_wdt_chip = {
+	.name		= "bcm6345-timer",
+	.irq_enable	= bcm6345_timer_irq_noop,
+	.irq_disable	= bcm6345_timer_irq_noop,
+};
+
+static int bcm6345_timer_map(struct irq_domain *d, unsigned int virq,
+			     irq_hw_number_t hwirq)
+{
+	struct bcm6345_timer *timer = d->host_data;
+
+	if (hwirq < timer->variant.nr_timers) {
+		irq_set_chip_and_handler(virq, &bcm6345_timer_chip,
+			handle_fasteoi_irq);
+	} else {
+		/* Watchdog interrupt can't be disabled or acked */
+		irq_set_chip_and_handler(virq, &bcm6345_timer_wdt_chip,
+			handle_simple_irq);
+	}
+	irq_set_chip_data(virq, timer);
+	return 0;
+}
+
+static const struct irq_domain_ops bcm6345_timer_domain_ops = {
+	.xlate	= irq_domain_xlate_onecell,
+	.map	= bcm6345_timer_map,
+};
+
+static int __init bcm63xx_timer_init(struct device_node *node,
+	const char *name, const struct bcm6345_timer_variant *variant)
+{
+	struct bcm6345_timer *timer;
+	unsigned int i;
+	int ret;
+
+	timer = kzalloc(sizeof(*timer), GFP_KERNEL);
+	if (!timer)
+		return -ENOMEM;
+
+	raw_spin_lock_init(&timer->lock);
+	memcpy(&timer->variant, variant, sizeof(*variant));
+	/* The watchdog warning event is the next interrupt bit
+	 * after the timers. It has different control/countdown
+	 * registers, handled by the watchdog driver.
+	 */
+	timer->nr_interrupts = timer->variant.nr_timers + 1;
+
+	timer->irq = irq_of_parse_and_map(node, 0);
+	if (!timer->irq) {
+		pr_err("unable to map parent IRQ\n");
+		ret = -EINVAL;
+		goto free_timer;
+	}
+
+	timer->base = of_iomap(node, 0);
+	if (!timer->base) {
+		pr_err("unable to map registers\n");
+		ret = -ENOMEM;
+		goto free_timer;
+	}
+
+	timer->domain = irq_domain_add_linear(node, timer->nr_interrupts,
+					&bcm6345_timer_domain_ops, timer);
+	if (!timer->domain) {
+		pr_err("unable to add IRQ domain");
+		ret = -ENOMEM;
+		goto unmap_io;
+	}
+
+	/* Mask all interrupts and stop all timers */
+	bcm6345_timer_write_int_enable(timer, 0);
+	for (i = 0; i < timer->variant.nr_timers; i++)
+		bcm6345_timer_stop(timer, i);
+
+	irq_set_chained_handler_and_data(timer->irq,
+					bcm6345_timer_interrupt, timer);
+
+	pr_info("registered %s L2 (timer) intc at MMIO 0x%p (irq = %d, IRQs: %d)\n",
+			name, timer->base, timer->irq, timer->nr_interrupts);
+	return 0;
+
+unmap_io:
+	iounmap(timer->base);
+free_timer:
+	kfree(timer);
+	return ret;
+}
+
+static int __init bcm6318_timer_init(struct device_node *node,
+				      struct device_node *parent)
+{
+	return bcm63xx_timer_init(node, "BCM6318", &timer_bcm6318);
+}
+
+static int __init bcm6345_timer_init(struct device_node *node,
+				      struct device_node *parent)
+{
+	return bcm63xx_timer_init(node, "BCM6345", &timer_bcm6345);
+}
+
+IRQCHIP_DECLARE(bcm6318_l2_timer, "brcm,bcm6318-timer", bcm6318_timer_init);
+IRQCHIP_DECLARE(bcm6345_l2_timer, "brcm,bcm6345-timer", bcm6345_timer_init);
-- 
2.1.4

-- 
Simon Arlott
--
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] 95+ messages in thread

* Re: [PATCH (v5) 3/11] MIPS: bmips: Add bcm6345-l2-timer interrupt controller
@ 2015-12-01  0:22                   ` Guenter Roeck
  0 siblings, 0 replies; 95+ messages in thread
From: Guenter Roeck @ 2015-12-01  0:22 UTC (permalink / raw)
  To: Simon Arlott, Thomas Gleixner
  Cc: Florian Fainelli, MIPS Mailing List, Jonas Gorski, devicetree,
	Ralf Baechle, Jason Cooper, Marc Zyngier, Kevin Cernekee,
	Wim Van Sebroeck, Miguel Gaio, Maxime Bizon,
	Linux Kernel Mailing List, linux-watchdog, Rob Herring,
	Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala

On 11/28/2015 04:26 AM, Simon Arlott wrote:
> Add the BCM6345/BCM6318 timer as an interrupt controller so that it can be
> used by the watchdog to warn that its timer will expire soon.
>
> Support for clocksource/clockevents is not implemented as the timer
> interrupt is not per CPU (except on the BCM6318) and the MIPS clock is
> better. This could be added later if required without changing the device
> tree binding.
>
> Signed-off-by: Simon Arlott <simon@fire.lp0.eu>

Hi Simon,

can you please re-send the entire series, with all Acked-by:/Reviewed-by:
tags as appropriate ?

Thanks,
Guenter


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

* Re: [PATCH (v5) 3/11] MIPS: bmips: Add bcm6345-l2-timer interrupt controller
@ 2015-12-01  0:22                   ` Guenter Roeck
  0 siblings, 0 replies; 95+ messages in thread
From: Guenter Roeck @ 2015-12-01  0:22 UTC (permalink / raw)
  To: Simon Arlott, Thomas Gleixner
  Cc: Florian Fainelli, MIPS Mailing List, Jonas Gorski,
	devicetree-u79uwXL29TY76Z2rM5mHXA, Ralf Baechle, Jason Cooper,
	Marc Zyngier, Kevin Cernekee, Wim Van Sebroeck, Miguel Gaio,
	Maxime Bizon, Linux Kernel Mailing List,
	linux-watchdog-u79uwXL29TY76Z2rM5mHXA, Rob Herring, Pawel Moll,
	Mark Rutland, Ian Campbell, Kumar Gala

On 11/28/2015 04:26 AM, Simon Arlott wrote:
> Add the BCM6345/BCM6318 timer as an interrupt controller so that it can be
> used by the watchdog to warn that its timer will expire soon.
>
> Support for clocksource/clockevents is not implemented as the timer
> interrupt is not per CPU (except on the BCM6318) and the MIPS clock is
> better. This could be added later if required without changing the device
> tree binding.
>
> Signed-off-by: Simon Arlott <simon-A6De1vDTPLDsq35pWSNszA@public.gmane.org>

Hi Simon,

can you please re-send the entire series, with all Acked-by:/Reviewed-by:
tags as appropriate ?

Thanks,
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] 95+ messages in thread

* Re: [PATCH (v5) 3/11] MIPS: bmips: Add bcm6345-l2-timer interrupt controller
  2015-12-01  0:22                   ` Guenter Roeck
  (?)
@ 2016-05-09 12:01                   ` Álvaro Fernández Rojas
  2016-05-09 13:06                       ` Guenter Roeck
  -1 siblings, 1 reply; 95+ messages in thread
From: Álvaro Fernández Rojas @ 2016-05-09 12:01 UTC (permalink / raw)
  To: Guenter Roeck, Simon Arlott, Thomas Gleixner
  Cc: Ralf Baechle, Linux Kernel Mailing List, Mark Rutland,
	Florian Fainelli, Kumar Gala, Kevin Cernekee, MIPS Mailing List,
	Wim Van Sebroeck, Miguel Gaio, Marc Zyngier, Jonas Gorski,
	Pawel Moll, Rob Herring, devicetree, Maxime Bizon, Ian Campbell,
	linux-watchdog, Jason Cooper

[-- Attachment #1: Type: text/plain, Size: 1708 bytes --]

Hello Guenter,
Are there any other issues preventing this patches from being merged?
Regards,Álvaro.
    _____________________________
From: Guenter Roeck <linux@roeck-us.net>
Sent: martes, diciembre 1, 2015 1:23 a. m.
Subject: Re: [PATCH (v5) 3/11] MIPS: bmips: Add bcm6345-l2-timer interrupt controller
To: Simon Arlott <simon@fire.lp0.eu>, Thomas Gleixner <tglx@linutronix.de>
Cc: Ralf Baechle <ralf@linux-mips.org>, Linux Kernel Mailing List <linux-kernel@vger.kernel.org>, Mark Rutland <mark.rutland@arm.com>, Florian Fainelli <f.fainelli@gmail.com>, Ian Campbell <ijc+devicetree@hellion.org.uk>, Kevin Cernekee <cernekee@gmail.com>, MIPS Mailing List <linux-mips@linux-mips.org>, Kumar Gala <galak@codeaurora.org>, Miguel Gaio <miguel.gaio@efixo.com>, Jonas Gorski <jogo@openwrt.org>, Marc Zyngier <marc.zyngier@arm.com>, Pawel Moll <pawel.moll@arm.com>, Rob Herring <robh+dt@kernel.org>,  <devicetree@vger.kernel.org>, Maxime Bizon <mbizon@freebox.fr>, Wim Van Sebroeck <wim@iguana.be>,  <linux-watchdog@vger.kernel.org>, Jason Cooper <jason@lakedaemon.net>


On 11/28/2015 04:26 AM, Simon Arlott wrote:
> Add the BCM6345/BCM6318 timer as an interrupt controller so that it can be
> used by the watchdog to warn that its timer will expire soon.
>
> Support for clocksource/clockevents is not implemented as the timer
> interrupt is not per CPU (except on the BCM6318) and the MIPS clock is
> better. This could be added later if required without changing the device
> tree binding.
>
> Signed-off-by: Simon Arlott <simon@fire.lp0.eu>

Hi Simon,

can you please re-send the entire series, with all Acked-by:/Reviewed-by:
tags as appropriate ?

Thanks,
Guenter





  

[-- Attachment #2: Type: text/html, Size: 5530 bytes --]

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

* Re: [PATCH (v5) 3/11] MIPS: bmips: Add bcm6345-l2-timer interrupt controller
@ 2016-05-09 13:06                       ` Guenter Roeck
  0 siblings, 0 replies; 95+ messages in thread
From: Guenter Roeck @ 2016-05-09 13:06 UTC (permalink / raw)
  To: Álvaro Fernández Rojas, Simon Arlott, Thomas Gleixner
  Cc: Ralf Baechle, Linux Kernel Mailing List, Mark Rutland,
	Florian Fainelli, Kumar Gala, Kevin Cernekee, MIPS Mailing List,
	Wim Van Sebroeck, Miguel Gaio, Marc Zyngier, Jonas Gorski,
	Pawel Moll, Rob Herring, devicetree, Maxime Bizon, Ian Campbell,
	linux-watchdog, Jason Cooper

On 05/09/2016 05:01 AM, Álvaro Fernández Rojas wrote:
> Hello Guenter,
>
> Are there any other issues preventing this patches from being merged?
>

My major problem/concern is the interrupt handling and the repeated
restart of the hardware timer with continuously reduced timeouts.
This appears fragile, and it doesn't really make sense from a system
level point of view. Unfortunately, I don't have a data sheet for
the chip, nor the means or the time to test it myself. So we are pretty
much in limbo, unless someone from Broadcom confirms that, yes, this is
the one and expected means to program this watchdog.

Guenter

> Regards,
> Álvaro.
> _____________________________
> From: Guenter Roeck <linux@roeck-us.net <mailto:linux@roeck-us.net>>
> Sent: martes, diciembre 1, 2015 1:23 a. m.
> Subject: Re: [PATCH (v5) 3/11] MIPS: bmips: Add bcm6345-l2-timer interrupt controller
> To: Simon Arlott <simon@fire.lp0.eu <mailto:simon@fire.lp0.eu>>, Thomas Gleixner <tglx@linutronix.de <mailto:tglx@linutronix.de>>
> Cc: Ralf Baechle <ralf@linux-mips.org <mailto:ralf@linux-mips.org>>, Linux Kernel Mailing List <linux-kernel@vger.kernel.org <mailto:linux-kernel@vger.kernel.org>>, Mark Rutland <mark.rutland@arm.com <mailto:mark.rutland@arm.com>>, Florian Fainelli <f.fainelli@gmail.com <mailto:f.fainelli@gmail.com>>, Ian Campbell <ijc+devicetree@hellion.org.uk <mailto:ijc+devicetree@hellion.org.uk>>, Kevin Cernekee <cernekee@gmail.com <mailto:cernekee@gmail.com>>, MIPS Mailing List <linux-mips@linux-mips.org <mailto:linux-mips@linux-mips.org>>, Kumar Gala <galak@codeaurora.org <mailto:galak@codeaurora.org>>, Miguel Gaio <miguel.gaio@efixo.com <mailto:miguel.gaio@efixo.com>>, Jonas Gorski <jogo@openwrt.org <mailto:jogo@openwrt.org>>, Marc Zyngier <marc.zyngier@arm.com <mailto:marc.zyngier@arm.com>>, Pawel Moll <pawel.moll@arm.com <mailto:pawel.moll@arm.com>>, Rob Herring <robh+dt@kernel.org <mailto:robh+dt@kernel.org>>, <devicetree@vger.kernel.org <mailto:devicetree@vger.kernel.org>>, Maxime
> Bizon <mbizon@freebox.fr <mailto:mbizon@freebox.fr>>, Wim Van Sebroeck <wim@iguana.be <mailto:wim@iguana.be>>, <linux-watchdog@vger.kernel.org <mailto:linux-watchdog@vger.kernel.org>>, Jason Cooper <jason@lakedaemon.net <mailto:jason@lakedaemon.net>>
>
>
> On 11/28/2015 04:26 AM, Simon Arlott wrote:
>  > Add the BCM6345/BCM6318 timer as an interrupt controller so that it can be
>  > used by the watchdog to warn that its timer will expire soon.
>  >
>  > Support for clocksource/clockevents is not implemented as the timer
>  > interrupt is not per CPU (except on the BCM6318) and the MIPS clock is
>  > better. This could be added later if required without changing the device
>  > tree binding.
>  >
>  > Signed-off-by: Simon Arlott <simon@fire.lp0.eu <mailto:simon@fire.lp0.eu>>
>
> Hi Simon,
>
> can you please re-send the entire series, with all Acked-by:/Reviewed-by:
> tags as appropriate ?
>
> Thanks,
> Guenter
>
>
>
>

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

* Re: [PATCH (v5) 3/11] MIPS: bmips: Add bcm6345-l2-timer interrupt controller
@ 2016-05-09 13:06                       ` Guenter Roeck
  0 siblings, 0 replies; 95+ messages in thread
From: Guenter Roeck @ 2016-05-09 13:06 UTC (permalink / raw)
  To: Álvaro Fernández Rojas, Simon Arlott, Thomas Gleixner
  Cc: Ralf Baechle, Linux Kernel Mailing List, Mark Rutland,
	Florian Fainelli, Kumar Gala, Kevin Cernekee, MIPS Mailing List,
	Wim Van Sebroeck, Miguel Gaio, Marc Zyngier, Jonas Gorski,
	Pawel Moll, Rob Herring, devicetree-u79uwXL29TY76Z2rM5mHXA,
	Maxime Bizon, Ian Campbell,
	linux-watchdog-u79uwXL29TY76Z2rM5mHXA, Jason Cooper

On 05/09/2016 05:01 AM, Álvaro Fernández Rojas wrote:
> Hello Guenter,
>
> Are there any other issues preventing this patches from being merged?
>

My major problem/concern is the interrupt handling and the repeated
restart of the hardware timer with continuously reduced timeouts.
This appears fragile, and it doesn't really make sense from a system
level point of view. Unfortunately, I don't have a data sheet for
the chip, nor the means or the time to test it myself. So we are pretty
much in limbo, unless someone from Broadcom confirms that, yes, this is
the one and expected means to program this watchdog.

Guenter

> Regards,
> Álvaro.
> _____________________________
> From: Guenter Roeck <linux-0h96xk9xTtrk1uMJSBkQmQ@public.gmane.org <mailto:linux-0h96xk9xTtrk1uMJSBkQmQ@public.gmane.org>>
> Sent: martes, diciembre 1, 2015 1:23 a. m.
> Subject: Re: [PATCH (v5) 3/11] MIPS: bmips: Add bcm6345-l2-timer interrupt controller
> To: Simon Arlott <simon-A6De1vDTPLDsq35pWSNszA@public.gmane.org <mailto:simon-A6De1vDTPLDsq35pWSNszA@public.gmane.org>>, Thomas Gleixner <tglx-hfZtesqFncYOwBW4kG4KsQ@public.gmane.org <mailto:tglx-hfZtesqFncYOwBW4kG4KsQ@public.gmane.org>>
> Cc: Ralf Baechle <ralf-6z/3iImG2C8G8FEW9MqTrA@public.gmane.org <mailto:ralf-6z/3iImG2C8G8FEW9MqTrA@public.gmane.org>>, Linux Kernel Mailing List <linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org <mailto:linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org>>, Mark Rutland <mark.rutland-5wv7dgnIgG8@public.gmane.org <mailto:mark.rutland-5wv7dgnIgG8@public.gmane.org>>, Florian Fainelli <f.fainelli-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org <mailto:f.fainelli-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>>, Ian Campbell <ijc+devicetree-KcIKpvwj1kUDXYZnReoRVg@public.gmane.org <mailto:ijc+devicetree-KcIKpvwj1kUDXYZnReoRVg@public.gmane.org>>, Kevin Cernekee <cernekee-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org <mailto:cernekee-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>>, MIPS Mailing List <linux-mips@linux-mips.org <mailto:linux-mips-6z/3iImG2C8G8FEW9MqTrA@public.gmane.org>>, Kumar Gala <galak-sgV2jX0FEOJiIJoWrK/4GQ@public.gmane.orgg <mailto:galak-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>>, Miguel Gaio <miguel.gaio-HH44TBFINEIAvxtiuMwx3w@public.gmane.org <mailto:miguel.gaio-HH44TBFINEIAvxtiuMwx3w@public.gmane.org>>, Jonas Gorski <jogo-p3rKhJxN3npAfugRpC6u6w@public.gmane.org <mailto:jogo-p3rKhJxN3npAfugRpC6u6w@public.gmane.org>>, Marc Zyngier <marc.zyngier-5wv7dgnIgG8@public.gmane.org <mailto:marc.zyngier-5wv7dgnIgG8@public.gmane.org>>, Pawel Moll <pawel.moll-5wv7dgnIgG8@public.gmane.org <mailto:pawel.moll-AbSShOkvfpQ@public.gmane.orgm>>, Rob Herring <robh+dt-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org <mailto:robh+dt-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>>, <devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org <mailto:devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org>>, Maxime
> Bizon <mbizon-MmRyKUhfbQ9GWvitb5QawA@public.gmane.org <mailto:mbizon-MmRyKUhfbQ9GWvitb5QawA@public.gmane.org>>, Wim Van Sebroeck <wim-IQzOog9fTRqzQB+pC5nmwQ@public.gmane.org <mailto:wim-IQzOog9fTRqzQB+pC5nmwQ@public.gmane.org>>, <linux-watchdog-u79uwXL29TbrhsbdSgBK9A@public.gmane.orgrg <mailto:linux-watchdog-u79uwXL29TY76Z2rM5mHXA@public.gmane.org>>, Jason Cooper <jason@lakedaemon.net <mailto:jason-NLaQJdtUoK4Be96aLqz0jA@public.gmane.org>>
>
>
> On 11/28/2015 04:26 AM, Simon Arlott wrote:
>  > Add the BCM6345/BCM6318 timer as an interrupt controller so that it can be
>  > used by the watchdog to warn that its timer will expire soon.
>  >
>  > Support for clocksource/clockevents is not implemented as the timer
>  > interrupt is not per CPU (except on the BCM6318) and the MIPS clock is
>  > better. This could be added later if required without changing the device
>  > tree binding.
>  >
>  > Signed-off-by: Simon Arlott <simon-A6De1vDTPLDsq35pWSNszA@public.gmane.org <mailto:simon@fire.lp0.eu>>
>
> Hi Simon,
>
> can you please re-send the entire series, with all Acked-by:/Reviewed-by:
> tags as appropriate ?
>
> Thanks,
> Guenter
>
>
>
>

--
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	[flat|nested] 95+ messages in thread

* Re: [PATCH 2/4] MIPS: bmips: Add bcm6345-l2-timer interrupt controller
  2016-05-09 13:06                       ` Guenter Roeck
  (?)
@ 2016-05-11  6:40                       ` Álvaro Fernández Rojas
  -1 siblings, 0 replies; 95+ messages in thread
From: Álvaro Fernández Rojas @ 2016-05-11  6:40 UTC (permalink / raw)
  To: Guenter Roeck, Florian Fainelli, fainelli
  Cc: Simon Arlott, Thomas Gleixner, Ralf Baechle,
	Linux Kernel Mailing List, Mark Rutland, Kumar Gala,
	Kevin Cernekee, MIPS Mailing List, Wim Van Sebroeck, Miguel Gaio,
	Marc Zyngier, Jonas Gorski, Pawel Moll, Rob Herring, devicetree,
	Maxime Bizon, Ian Campbell, linux-watchdog, Jason Cooper

[-- Attachment #1: Type: text/plain, Size: 1637 bytes --]

El lunes, 9 de mayo de 2016, Guenter Roeck <linux@roeck-us.net> escribió:

> On 05/09/2016 05:01 AM, Álvaro Fernández Rojas wrote:
>
>> Hello Guenter,
>>
>> Are there any other issues preventing this patches from being merged?
>>
>>
> My major problem/concern is the interrupt handling and the repeated
> restart of the hardware timer with continuously reduced timeouts.
> This appears fragile, and it doesn't really make sense from a system
> level point of view. Unfortunately, I don't have a data sheet for
> the chip, nor the means or the time to test it myself. So we are pretty
> much in limbo, unless someone from Broadcom confirms that, yes, this is
> the one and expected means to program this watchdog.


As far as I know Florian is now working for Broadcom and he may be able to
give us a hand on this matter :).


>
> Guenter
>
> Regards,
>> Álvaro.
>>
>> On 11/28/2015 04:26 AM, Simon Arlott wrote:
>>  > Add the BCM6345/BCM6318 timer as an interrupt controller so that it
>> can be
>>  > used by the watchdog to warn that its timer will expire soon.
>>  >
>>  > Support for clocksource/clockevents is not implemented as the timer
>>  > interrupt is not per CPU (except on the BCM6318) and the MIPS clock is
>>  > better. This could be added later if required without changing the
>> device
>>  > tree binding.
>>  >
>>  > Signed-off-by: Simon Arlott <simon@fire.lp0.eu <mailto:
>> simon@fire.lp0.eu>>
>>
>> Hi Simon,
>>
>> can you please re-send the entire series, with all Acked-by:/Reviewed-by:
>> tags as appropriate ?
>>
>> Thanks,
>> Guenter
>>
>>
>>
>>
>>
>

[-- Attachment #2: Type: text/html, Size: 2306 bytes --]

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

end of thread, other threads:[~2016-05-11  6:40 UTC | newest]

Thread overview: 95+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-11-21 19:02 [PATCH 1/4] clocksource: Add brcm,bcm6345-timer device tree binding Simon Arlott
2015-11-21 19:02 ` Simon Arlott
2015-11-21 19:03 ` [PATCH 2/4] MIPS: bmips: Add bcm6345-l2-timer interrupt controller Simon Arlott
2015-11-21 19:04 ` [PATCH 3/4] watchdog: Add brcm,bcm6345-wdt device tree binding Simon Arlott
2015-11-22 22:13   ` Rob Herring
2015-11-22 22:13     ` Rob Herring
2015-11-21 19:05 ` [PATCH 4/4] MIPS: bmips: Convert bcm63xx_wdt to use WATCHDOG_CORE Simon Arlott
2015-11-21 19:05   ` Simon Arlott
2015-11-21 21:32   ` Guenter Roeck
2015-11-21 21:44     ` Simon Arlott
2015-11-21 21:44       ` Simon Arlott
2015-11-22  2:32       ` Guenter Roeck
2015-11-22 14:02         ` [PATCH 4/10] (Was: [PATCH 4/4]) " Simon Arlott
2015-11-22 14:05           ` [PATCH 4/10] watchdog: bcm63xx_wdt: Handle hardware interrupt and remove software timer Simon Arlott
2015-11-24 18:21             ` Guenter Roeck
2015-11-24 18:21               ` Guenter Roeck
2015-11-24 18:21               ` Guenter Roeck
2015-11-25 20:14               ` Jonas Gorski
2015-11-25 20:14                 ` Jonas Gorski
2015-11-25 20:28                 ` Simon Arlott
2015-11-25 20:28                   ` Simon Arlott
2015-11-25 22:33             ` [PATCH (v2) " Simon Arlott
2015-11-25 22:33               ` Simon Arlott
2015-11-22 14:06           ` [PATCH 5/10] watchdog: bcm63xx_wdt: Use WATCHDOG_CORE Simon Arlott
2015-11-22 14:06             ` Simon Arlott
2015-11-25  2:44             ` Guenter Roeck
2015-11-25  2:44               ` Guenter Roeck
2015-11-25 13:02               ` Simon Arlott
2015-11-25 14:10                 ` Guenter Roeck
2015-11-25 14:10                   ` Guenter Roeck
2015-11-25 19:43                   ` Simon Arlott
2015-11-25 19:43                     ` Simon Arlott
2015-11-25 22:40               ` [PATCH (v3) 5/11] " Simon Arlott
2015-11-25 22:40                 ` Simon Arlott
2015-11-22 14:07           ` [PATCH 6/10] watchdog: bcm63xx_wdt: Obtain watchdog clock HZ from "periph" clk Simon Arlott
2015-11-22 14:07             ` Simon Arlott
2015-11-23 15:02             ` Jonas Gorski
2015-11-23 15:02               ` Jonas Gorski
2015-11-23 18:19               ` Florian Fainelli
2015-11-23 18:19                 ` Florian Fainelli
2015-11-23 19:00                 ` Simon Arlott
2015-11-23 19:00                   ` Simon Arlott
2015-11-24 22:12             ` [PATCH (v2) " Simon Arlott
2015-11-24 22:42               ` Florian Fainelli
2015-11-24 22:42                 ` Florian Fainelli
2015-11-25 22:47                 ` [PATCH (v3) 6/11] " Simon Arlott
2015-11-25 22:47                   ` Simon Arlott
2015-11-22 14:09           ` [PATCH 7/10] watchdog: bcm63xx_wdt: Add get_timeleft function Simon Arlott
2015-11-22 14:09             ` Simon Arlott
2015-11-24 22:15             ` [PATCH (v2) " Simon Arlott
2015-11-24 22:15               ` Simon Arlott
2015-11-24 22:43               ` Florian Fainelli
2015-11-24 22:43                 ` Florian Fainelli
2015-11-25  2:51               ` Guenter Roeck
2015-11-25  2:51                 ` Guenter Roeck
2015-11-25  8:17                 ` Simon Arlott
2015-11-25  8:17                   ` Simon Arlott
2015-11-25 22:50                   ` [PATCH (v3) 7/11] " Simon Arlott
2015-11-25 22:50                     ` Simon Arlott
2015-11-25 22:54                     ` [PATCH (v4) " Simon Arlott
2015-11-25 22:54                       ` Simon Arlott
2015-11-25 22:57                       ` [PATCH (v4) 8/11] watchdog: bcm63xx_wdt: Warn if the watchdog is currently running Simon Arlott
2015-11-25 22:57                         ` Simon Arlott
2015-11-22 14:11           ` [PATCH 8/10] watchdog: bcm63xx_wdt: Remove dependency on mach-bcm63xx functions/defines Simon Arlott
2015-11-22 14:11             ` Simon Arlott
2015-11-22 14:12           ` [PATCH 9/10] watchdog: bcm63xx_wdt: Use bcm63xx_timer interrupt directly Simon Arlott
2015-11-22 14:12             ` Simon Arlott
2015-11-25 23:03             ` [PATCH (v2) 10/11] " Simon Arlott
2015-11-25 23:03               ` Simon Arlott
2015-11-22 14:14           ` [PATCH 10/10] watchdog: bcm63xx_wdt: Use brcm,bcm6345-wdt device tree binding Simon Arlott
2015-11-22 14:14             ` Simon Arlott
2015-11-25 23:09             ` [PATCH (v2) 11/11] " Simon Arlott
2015-11-25 23:09               ` Simon Arlott
2015-11-22 22:12 ` [PATCH 1/4] clocksource: Add brcm,bcm6345-timer " Rob Herring
2015-11-23 15:33 ` Jonas Gorski
2015-11-23 18:55   ` [PATCH (v2) 1/10] " Simon Arlott
2015-11-23 18:55     ` Simon Arlott
2015-11-23 18:57     ` [PATCH (v2) 2/10] MIPS: bmips: Add bcm6345-l2-timer interrupt controller Simon Arlott
2015-11-23 18:57       ` Simon Arlott
2015-11-24 22:10       ` [PATCH (v3) " Simon Arlott
2015-11-24 22:10         ` Simon Arlott
2015-11-24 22:36         ` Florian Fainelli
2015-11-24 22:36           ` Florian Fainelli
2015-11-26 22:32           ` [PATCH (v4) 2/11] " Simon Arlott
2015-11-27  8:37             ` Thomas Gleixner
2015-11-27  8:37               ` Thomas Gleixner
2015-11-28 12:26               ` [PATCH (v5) 3/11] " Simon Arlott
2015-11-28 12:26                 ` Simon Arlott
2015-12-01  0:22                 ` Guenter Roeck
2015-12-01  0:22                   ` Guenter Roeck
2016-05-09 12:01                   ` Álvaro Fernández Rojas
2016-05-09 13:06                     ` Guenter Roeck
2016-05-09 13:06                       ` Guenter Roeck
2016-05-11  6:40                       ` [PATCH 2/4] " Álvaro Fernández Rojas
2015-11-25  3:05     ` [PATCH (v2) 1/10] clocksource: Add brcm,bcm6345-timer device tree binding Rob Herring

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.