Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > linux.kernel > #1274735 > unrolled thread
| Started by | Simon Arlott <simon@fire.lp0.eu> |
|---|---|
| First post | 2015-11-21 20:10 +0100 |
| Last post | 2015-11-25 04:10 +0100 |
| Articles | 20 on this page of 50 — 7 participants |
Back to article view | Back to linux.kernel
[PATCH 1/4] clocksource: Add brcm,bcm6345-timer device tree binding Simon Arlott <simon@fire.lp0.eu> - 2015-11-21 20:10 +0100
[PATCH 2/4] MIPS: bmips: Add bcm6345-l2-timer interrupt controller Simon Arlott <simon@fire.lp0.eu> - 2015-11-21 20:10 +0100
[PATCH 4/4] MIPS: bmips: Convert bcm63xx_wdt to use WATCHDOG_CORE Simon Arlott <simon@fire.lp0.eu> - 2015-11-21 20:10 +0100
Re: [PATCH 4/4] MIPS: bmips: Convert bcm63xx_wdt to use WATCHDOG_CORE Guenter Roeck <linux@roeck-us.net> - 2015-11-21 22:40 +0100
Re: [PATCH 4/4] MIPS: bmips: Convert bcm63xx_wdt to use WATCHDOG_CORE Simon Arlott <simon@fire.lp0.eu> - 2015-11-21 22:50 +0100
Re: [PATCH 4/4] MIPS: bmips: Convert bcm63xx_wdt to use WATCHDOG_CORE Guenter Roeck <linux@roeck-us.net> - 2015-11-22 03:40 +0100
[PATCH 6/10] watchdog: bcm63xx_wdt: Obtain watchdog clock HZ from "periph" clk Simon Arlott <simon@fire.lp0.eu> - 2015-11-22 15:10 +0100
Re: [PATCH 6/10] watchdog: bcm63xx_wdt: Obtain watchdog clock HZ from "periph" clk Jonas Gorski <jogo@openwrt.org> - 2015-11-23 16:10 +0100
Re: [PATCH 6/10] watchdog: bcm63xx_wdt: Obtain watchdog clock HZ from "periph" clk Florian Fainelli <f.fainelli@gmail.com> - 2015-11-23 19:30 +0100
Re: [PATCH 6/10] watchdog: bcm63xx_wdt: Obtain watchdog clock HZ from "periph" clk Simon Arlott <simon@fire.lp0.eu> - 2015-11-23 20:10 +0100
[PATCH (v2) 6/10] watchdog: bcm63xx_wdt: Obtain watchdog clock HZ from "periph" clk "Simon Arlott" <simon@fire.lp0.eu> - 2015-11-24 23:20 +0100
Re: [PATCH (v2) 6/10] watchdog: bcm63xx_wdt: Obtain watchdog clock HZ from "periph" clk Florian Fainelli <f.fainelli@gmail.com> - 2015-11-24 23:50 +0100
[PATCH (v3) 6/11] watchdog: bcm63xx_wdt: Obtain watchdog clock HZ from "periph" clk Simon Arlott <simon@fire.lp0.eu> - 2015-11-25 23:50 +0100
[PATCH 7/10] watchdog: bcm63xx_wdt: Add get_timeleft function Simon Arlott <simon@fire.lp0.eu> - 2015-11-22 15:10 +0100
[PATCH (v2) 7/10] watchdog: bcm63xx_wdt: Add get_timeleft function "Simon Arlott" <simon@fire.lp0.eu> - 2015-11-24 23:20 +0100
Re: [PATCH (v2) 7/10] watchdog: bcm63xx_wdt: Add get_timeleft function Florian Fainelli <f.fainelli@gmail.com> - 2015-11-24 23:50 +0100
Re: [PATCH (v2) 7/10] watchdog: bcm63xx_wdt: Add get_timeleft function Guenter Roeck <linux@roeck-us.net> - 2015-11-25 04:00 +0100
Re: [PATCH (v2) 7/10] watchdog: bcm63xx_wdt: Add get_timeleft function Simon Arlott <simon@fire.lp0.eu> - 2015-11-25 09:20 +0100
[PATCH (v4) 7/11] watchdog: bcm63xx_wdt: Add get_timeleft function Simon Arlott <simon@fire.lp0.eu> - 2015-11-26 00:00 +0100
[PATCH (v4) 8/11] watchdog: bcm63xx_wdt: Warn if the watchdog is currently running Simon Arlott <simon@fire.lp0.eu> - 2015-11-26 00:00 +0100
[PATCH (v3) 7/11] watchdog: bcm63xx_wdt: Add get_timeleft function Simon Arlott <simon@fire.lp0.eu> - 2015-11-26 00:00 +0100
Re: [PATCH 4/10] (Was: [PATCH 4/4]) MIPS: bmips: Convert bcm63xx_wdt to use WATCHDOG_CORE Simon Arlott <simon@fire.lp0.eu> - 2015-11-22 15:10 +0100
[PATCH 5/10] watchdog: bcm63xx_wdt: Use WATCHDOG_CORE Simon Arlott <simon@fire.lp0.eu> - 2015-11-22 15:10 +0100
Re: [PATCH 5/10] watchdog: bcm63xx_wdt: Use WATCHDOG_CORE Guenter Roeck <linux@roeck-us.net> - 2015-11-25 03:50 +0100
Re: [PATCH 5/10] watchdog: bcm63xx_wdt: Use WATCHDOG_CORE "Simon Arlott" <simon@fire.lp0.eu> - 2015-11-25 14:10 +0100
Re: [PATCH 5/10] watchdog: bcm63xx_wdt: Use WATCHDOG_CORE Guenter Roeck <linux@roeck-us.net> - 2015-11-25 15:20 +0100
Re: [PATCH 5/10] watchdog: bcm63xx_wdt: Use WATCHDOG_CORE Simon Arlott <simon@fire.lp0.eu> - 2015-11-25 20:50 +0100
[PATCH (v3) 5/11] watchdog: bcm63xx_wdt: Use WATCHDOG_CORE Simon Arlott <simon@fire.lp0.eu> - 2015-11-25 23:50 +0100
[PATCH 4/10] watchdog: bcm63xx_wdt: Handle hardware interrupt and remove software timer Simon Arlott <simon@fire.lp0.eu> - 2015-11-22 15:10 +0100
Re: [PATCH 4/10] watchdog: bcm63xx_wdt: Handle hardware interrupt and remove software timer Guenter Roeck <linux@roeck-us.net> - 2015-11-24 19:30 +0100
Re: [PATCH 4/10] watchdog: bcm63xx_wdt: Handle hardware interrupt and remove software timer Jonas Gorski <jogo@openwrt.org> - 2015-11-25 21:20 +0100
Re: [PATCH 4/10] watchdog: bcm63xx_wdt: Handle hardware interrupt and remove software timer Simon Arlott <simon@fire.lp0.eu> - 2015-11-25 21:30 +0100
[PATCH (v2) 4/10] watchdog: bcm63xx_wdt: Handle hardware interrupt and remove software timer Simon Arlott <simon@fire.lp0.eu> - 2015-11-25 23:40 +0100
[PATCH 8/10] watchdog: bcm63xx_wdt: Remove dependency on mach-bcm63xx functions/defines Simon Arlott <simon@fire.lp0.eu> - 2015-11-22 15:20 +0100
[PATCH 10/10] watchdog: bcm63xx_wdt: Use brcm,bcm6345-wdt device tree binding Simon Arlott <simon@fire.lp0.eu> - 2015-11-22 15:20 +0100
[PATCH (v2) 11/11] watchdog: bcm63xx_wdt: Use brcm,bcm6345-wdt device tree binding Simon Arlott <simon@fire.lp0.eu> - 2015-11-26 00:20 +0100
[PATCH 9/10] watchdog: bcm63xx_wdt: Use bcm63xx_timer interrupt directly Simon Arlott <simon@fire.lp0.eu> - 2015-11-22 15:20 +0100
[PATCH (v2) 10/11] watchdog: bcm63xx_wdt: Use bcm63xx_timer interrupt directly Simon Arlott <simon@fire.lp0.eu> - 2015-11-26 00:10 +0100
[PATCH 3/4] watchdog: Add brcm,bcm6345-wdt device tree binding Simon Arlott <simon@fire.lp0.eu> - 2015-11-21 20:10 +0100
Re: [PATCH 3/4] watchdog: Add brcm,bcm6345-wdt device tree binding Rob Herring <robh@kernel.org> - 2015-11-22 23:20 +0100
Re: [PATCH 1/4] clocksource: Add brcm,bcm6345-timer device tree binding Rob Herring <robh@kernel.org> - 2015-11-22 23:20 +0100
Re: [PATCH 1/4] clocksource: Add brcm,bcm6345-timer device tree binding Jonas Gorski <jogo@openwrt.org> - 2015-11-23 16:40 +0100
[PATCH (v2) 2/10] MIPS: bmips: Add bcm6345-l2-timer interrupt controller Simon Arlott <simon@fire.lp0.eu> - 2015-11-23 20:00 +0100
[PATCH (v3) 2/10] MIPS: bmips: Add bcm6345-l2-timer interrupt controller "Simon Arlott" <simon@fire.lp0.eu> - 2015-11-24 23:20 +0100
Re: [PATCH (v3) 2/10] MIPS: bmips: Add bcm6345-l2-timer interrupt controller Florian Fainelli <f.fainelli@gmail.com> - 2015-11-24 23:40 +0100
[PATCH (v4) 2/11] MIPS: bmips: Add bcm6345-l2-timer interrupt controller Simon Arlott <simon@fire.lp0.eu> - 2015-11-26 23:40 +0100
Re: [PATCH (v4) 2/11] MIPS: bmips: Add bcm6345-l2-timer interrupt controller Thomas Gleixner <tglx@linutronix.de> - 2015-11-27 09:40 +0100
[PATCH (v5) 3/11] MIPS: bmips: Add bcm6345-l2-timer interrupt controller Simon Arlott <simon@fire.lp0.eu> - 2015-11-28 13:30 +0100
[PATCH (v2) 1/10] clocksource: Add brcm,bcm6345-timer device tree binding Simon Arlott <simon@fire.lp0.eu> - 2015-11-23 20:00 +0100
Re: [PATCH (v2) 1/10] clocksource: Add brcm,bcm6345-timer device tree binding Rob Herring <robh@kernel.org> - 2015-11-25 04:10 +0100
Page 1 of 3 [1] 2 3 Next page →
| From | Simon Arlott <simon@fire.lp0.eu> |
|---|---|
| Date | 2015-11-21 20:10 +0100 |
| Subject | [PATCH 1/4] clocksource: Add brcm,bcm6345-timer device tree binding |
| Message-ID | <qxlHP-16F-1@gated-at.bofh.it> |
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
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
[toc] | [next] | [standalone]
| From | Simon Arlott <simon@fire.lp0.eu> |
|---|---|
| Date | 2015-11-21 20:10 +0100 |
| Subject | [PATCH 2/4] MIPS: bmips: Add bcm6345-l2-timer interrupt controller |
| Message-ID | <qxlHP-16F-3@gated-at.bofh.it> |
| In reply to | #1274735 |
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
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
[toc] | [prev] | [next] | [standalone]
| From | Simon Arlott <simon@fire.lp0.eu> |
|---|---|
| Date | 2015-11-21 20:10 +0100 |
| Subject | [PATCH 4/4] MIPS: bmips: Convert bcm63xx_wdt to use WATCHDOG_CORE |
| Message-ID | <qxlHP-16F-7@gated-at.bofh.it> |
| In reply to | #1274735 |
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
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
[toc] | [prev] | [next] | [standalone]
| From | Guenter Roeck <linux@roeck-us.net> |
|---|---|
| Date | 2015-11-21 22:40 +0100 |
| Subject | Re: [PATCH 4/4] MIPS: bmips: Convert bcm63xx_wdt to use WATCHDOG_CORE |
| Message-ID | <qxo30-2yb-3@gated-at.bofh.it> |
| In reply to | #1274737 |
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
>
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
[toc] | [prev] | [next] | [standalone]
| From | Simon Arlott <simon@fire.lp0.eu> |
|---|---|
| Date | 2015-11-21 22:50 +0100 |
| Subject | Re: [PATCH 4/4] MIPS: bmips: Convert bcm63xx_wdt to use WATCHDOG_CORE |
| Message-ID | <qxocG-2BX-5@gated-at.bofh.it> |
| In reply to | #1274757 |
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-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[toc] | [prev] | [next] | [standalone]
| From | Guenter Roeck <linux@roeck-us.net> |
|---|---|
| Date | 2015-11-22 03:40 +0100 |
| Subject | Re: [PATCH 4/4] MIPS: bmips: Convert bcm63xx_wdt to use WATCHDOG_CORE |
| Message-ID | <qxsJj-5JA-1@gated-at.bofh.it> |
| In reply to | #1274759 |
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 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[toc] | [prev] | [next] | [standalone]
| From | Simon Arlott <simon@fire.lp0.eu> |
|---|---|
| Date | 2015-11-22 15:10 +0100 |
| Subject | [PATCH 6/10] watchdog: bcm63xx_wdt: Obtain watchdog clock HZ from "periph" clk |
| Message-ID | <qxDv3-4PJ-5@gated-at.bofh.it> |
| In reply to | #1274777 |
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
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
[toc] | [prev] | [next] | [standalone]
| From | Jonas Gorski <jogo@openwrt.org> |
|---|---|
| Date | 2015-11-23 16:10 +0100 |
| Subject | Re: [PATCH 6/10] watchdog: bcm63xx_wdt: Obtain watchdog clock HZ from "periph" clk |
| Message-ID | <qy0UG-3v4-13@gated-at.bofh.it> |
| In reply to | #1274873 |
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 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[toc] | [prev] | [next] | [standalone]
| From | Florian Fainelli <f.fainelli@gmail.com> |
|---|---|
| Date | 2015-11-23 19:30 +0100 |
| Subject | Re: [PATCH 6/10] watchdog: bcm63xx_wdt: Obtain watchdog clock HZ from "periph" clk |
| Message-ID | <qy42e-5wu-7@gated-at.bofh.it> |
| In reply to | #1275465 |
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 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[toc] | [prev] | [next] | [standalone]
| From | Simon Arlott <simon@fire.lp0.eu> |
|---|---|
| Date | 2015-11-23 20:10 +0100 |
| Subject | Re: [PATCH 6/10] watchdog: bcm63xx_wdt: Obtain watchdog clock HZ from "periph" clk |
| Message-ID | <qy4EV-62g-11@gated-at.bofh.it> |
| In reply to | #1275703 |
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 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[toc] | [prev] | [next] | [standalone]
| From | "Simon Arlott" <simon@fire.lp0.eu> |
|---|---|
| Date | 2015-11-24 23:20 +0100 |
| Subject | [PATCH (v2) 6/10] watchdog: bcm63xx_wdt: Obtain watchdog clock HZ from "periph" clk |
| Message-ID | <qyu6n-5EM-9@gated-at.bofh.it> |
| In reply to | #1274873 |
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
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
[toc] | [prev] | [next] | [standalone]
| From | Florian Fainelli <f.fainelli@gmail.com> |
|---|---|
| Date | 2015-11-24 23:50 +0100 |
| Subject | Re: [PATCH (v2) 6/10] watchdog: bcm63xx_wdt: Obtain watchdog clock HZ from "periph" clk |
| Message-ID | <qyuzp-5RA-35@gated-at.bofh.it> |
| In reply to | #1276814 |
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 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[toc] | [prev] | [next] | [standalone]
| From | Simon Arlott <simon@fire.lp0.eu> |
|---|---|
| Date | 2015-11-25 23:50 +0100 |
| Subject | [PATCH (v3) 6/11] watchdog: bcm63xx_wdt: Obtain watchdog clock HZ from "periph" clk |
| Message-ID | <qyR2V-3ZJ-3@gated-at.bofh.it> |
| In reply to | #1276863 |
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
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
[toc] | [prev] | [next] | [standalone]
| From | Simon Arlott <simon@fire.lp0.eu> |
|---|---|
| Date | 2015-11-22 15:10 +0100 |
| Subject | [PATCH 7/10] watchdog: bcm63xx_wdt: Add get_timeleft function |
| Message-ID | <qxDv3-4PJ-7@gated-at.bofh.it> |
| In reply to | #1274777 |
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
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
[toc] | [prev] | [next] | [standalone]
| From | "Simon Arlott" <simon@fire.lp0.eu> |
|---|---|
| Date | 2015-11-24 23:20 +0100 |
| Subject | [PATCH (v2) 7/10] watchdog: bcm63xx_wdt: Add get_timeleft function |
| Message-ID | <qyu6o-5EM-27@gated-at.bofh.it> |
| In reply to | #1274874 |
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
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
[toc] | [prev] | [next] | [standalone]
| From | Florian Fainelli <f.fainelli@gmail.com> |
|---|---|
| Date | 2015-11-24 23:50 +0100 |
| Subject | Re: [PATCH (v2) 7/10] watchdog: bcm63xx_wdt: Add get_timeleft function |
| Message-ID | <qyuzo-5RA-25@gated-at.bofh.it> |
| In reply to | #1276819 |
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 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[toc] | [prev] | [next] | [standalone]
| From | Guenter Roeck <linux@roeck-us.net> |
|---|---|
| Date | 2015-11-25 04:00 +0100 |
| Subject | Re: [PATCH (v2) 7/10] watchdog: bcm63xx_wdt: Add get_timeleft function |
| Message-ID | <qyytk-8pA-11@gated-at.bofh.it> |
| In reply to | #1276819 |
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");
>
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
[toc] | [prev] | [next] | [standalone]
| From | Simon Arlott <simon@fire.lp0.eu> |
|---|---|
| Date | 2015-11-25 09:20 +0100 |
| Subject | Re: [PATCH (v2) 7/10] watchdog: bcm63xx_wdt: Add get_timeleft function |
| Message-ID | <qyDt0-3y9-27@gated-at.bofh.it> |
| In reply to | #1276985 |
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-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[toc] | [prev] | [next] | [standalone]
| From | Simon Arlott <simon@fire.lp0.eu> |
|---|---|
| Date | 2015-11-26 00:00 +0100 |
| Subject | [PATCH (v4) 7/11] watchdog: bcm63xx_wdt: Add get_timeleft function |
| Message-ID | <qyRcC-43u-1@gated-at.bofh.it> |
| In reply to | #1277090 |
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
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
[toc] | [prev] | [next] | [standalone]
| From | Simon Arlott <simon@fire.lp0.eu> |
|---|---|
| Date | 2015-11-26 00:00 +0100 |
| Subject | [PATCH (v4) 8/11] watchdog: bcm63xx_wdt: Warn if the watchdog is currently running |
| Message-ID | <qyRcC-43u-13@gated-at.bofh.it> |
| In reply to | #1277861 |
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
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
[toc] | [prev] | [next] | [standalone]
Page 1 of 3 [1] 2 3 Next page →
Back to top | Article view | linux.kernel
csiph-web