This patch series improves the watchdog driver used on the Broadcom
bcm47xx SoCs.
The watchdog driver does not access the functions directly any more,
but it registers as a platform device driver and ssb and bcma are
registering a device for this watchdog driver.
This also adds support for SoCs with a power management unit (PMU),
which have different clock rates.
This code is currently based on the wireless-testing/master tree by
John Linville, because there are some changes in ssb and bcma in that
tree queued for 3.8 which will conflict with these changes, if this
would be based on an other tree. I have no problem with rebasing this
onto any other tree.
Hauke Mehrtens (15):
watchdog: bcm47xx_wdt.c: convert to watchdog core api
watchdog: bcm47xx_wdt.c: use platform device
watchdog: bcm47xx_wdt.c: rename ops methods
watchdog: bcm47xx_wdt.c: rename wdt_timeout to timeout
watchdog: bcm47xx_wdt.c: add hard timer
bcma: add bcma_chipco_alp_clock
bcma: set the pmu watchdog if available
bcma: add methods for watchdog driver
bcma: register watchdog driver
ssb: get alp clock from devices with PMU
ssb: set the pmu watchdog if available
ssb: add methods for watchdog driver
ssb: extif: add check for max value in watchdog
ssb: extif: add methods for watchdog driver
ssb: register watchdog driver
drivers/bcma/bcma_private.h | 2 +
drivers/bcma/driver_chipcommon.c | 114 ++++++++-
drivers/bcma/main.c | 8 +
drivers/ssb/driver_chipcommon.c | 99 +++++++-
drivers/ssb/driver_chipcommon_pmu.c | 27 +++
drivers/ssb/driver_extif.c | 24 +-
drivers/ssb/embedded.c | 35 +++
drivers/ssb/main.c | 8 +
drivers/ssb/ssb_private.h | 31 +++
drivers/watchdog/Kconfig | 1 +
drivers/watchdog/bcm47xx_wdt.c | 339 ++++++++++++---------------
include/linux/bcm47xx_wdt.h | 28 +++
include/linux/bcma/bcma_driver_chipcommon.h | 7 +-
include/linux/ssb/ssb.h | 2 +
include/linux/ssb/ssb_driver_chipcommon.h | 5 +-
include/linux/ssb/ssb_driver_extif.h | 10 +-
16 files changed, 522 insertions(+), 218 deletions(-)
create mode 100644 include/linux/bcm47xx_wdt.h
--
1.7.10.4
Mostly all bcma based devices have a PMU and the PMU watchdog should be
used and not the old one in chip common. This patch also calculates the
maximal number the watchdog could be set to.
Signed-off-by: Hauke Mehrtens <[email protected]>
---
drivers/bcma/driver_chipcommon.c | 42 ++++++++++++++++++++++++++++++++++++--
1 file changed, 40 insertions(+), 2 deletions(-)
diff --git a/drivers/bcma/driver_chipcommon.c b/drivers/bcma/driver_chipcommon.c
index ef68553..7c132e5 100644
--- a/drivers/bcma/driver_chipcommon.c
+++ b/drivers/bcma/driver_chipcommon.c
@@ -31,6 +31,28 @@ static u32 bcma_chipco_alp_clock(struct bcma_drv_cc *cc)
return 20000000;
}
+static u32 bcma_chipco_watchdog_get_max_timer(struct bcma_drv_cc *cc)
+{
+ struct bcma_bus *bus = cc->core->bus;
+ u32 nb;
+
+ if (cc->capabilities & BCMA_CC_CAP_PMU) {
+ if (bus->chipinfo.id == BCMA_CHIP_ID_BCM4706)
+ nb = 32;
+ else if (cc->core->id.rev < 26)
+ nb = 16;
+ else
+ nb = (cc->core->id.rev >= 37) ? 32 : 24;
+ } else {
+ nb = 28;
+ }
+ if (nb == 32)
+ return 0xffffffff;
+ else
+ return (1 << nb) - 1;
+}
+
+
void bcma_core_chipcommon_early_init(struct bcma_drv_cc *cc)
{
if (cc->early_setup_done)
@@ -85,8 +107,24 @@ void bcma_core_chipcommon_init(struct bcma_drv_cc *cc)
/* Set chip watchdog reset timer to fire in 'ticks' backplane cycles */
void bcma_chipco_watchdog_timer_set(struct bcma_drv_cc *cc, u32 ticks)
{
- /* instant NMI */
- bcma_cc_write32(cc, BCMA_CC_WATCHDOG, ticks);
+ u32 maxt;
+ enum bcma_clkmode clkmode;
+
+ maxt = bcma_chipco_watchdog_get_max_timer(cc);
+ if (cc->capabilities & BCMA_CC_CAP_PMU) {
+ if (ticks == 1)
+ ticks = 2;
+ else if (ticks > maxt)
+ ticks = maxt;
+ bcma_cc_write32(cc, BCMA_CC_PMU_WATCHDOG, ticks);
+ } else {
+ clkmode = ticks ? BCMA_CLKMODE_FAST : BCMA_CLKMODE_DYNAMIC;
+ bcma_core_set_clockmode(cc->core, clkmode);
+ if (ticks > maxt)
+ ticks = maxt;
+ /* instant NMI */
+ bcma_cc_write32(cc, BCMA_CC_WATCHDOG, ticks);
+ }
}
void bcma_chipco_irq_mask(struct bcma_drv_cc *cc, u32 mask, u32 value)
--
1.7.10.4
The watchdog driver wants to set the watchdog timeout in ms and not in
ticks, which is depending on the SoC type and the clock.
Calculate the number of ticks per millisecond and provide two functions
for the watchdog driver. Also return the ticks or millisecond the timer
was set to in case the provided value was bigger than the max allowed
value.
Signed-off-by: Hauke Mehrtens <[email protected]>
---
drivers/bcma/driver_chipcommon.c | 38 ++++++++++++++++++++++++++-
include/linux/bcma/bcma_driver_chipcommon.h | 4 +--
2 files changed, 39 insertions(+), 3 deletions(-)
diff --git a/drivers/bcma/driver_chipcommon.c b/drivers/bcma/driver_chipcommon.c
index 7c132e5..1172226 100644
--- a/drivers/bcma/driver_chipcommon.c
+++ b/drivers/bcma/driver_chipcommon.c
@@ -10,6 +10,7 @@
*/
#include "bcma_private.h"
+#include <linux/bcm47xx_wdt.h>
#include <linux/export.h>
#include <linux/bcma/bcma.h>
@@ -52,6 +53,39 @@ static u32 bcma_chipco_watchdog_get_max_timer(struct bcma_drv_cc *cc)
return (1 << nb) - 1;
}
+static u32 bcma_chipco_watchdog_timer_set_wdt(struct bcm47xx_wdt *wdt,
+ u32 ticks)
+{
+ struct bcma_drv_cc *cc = bcm47xx_wdt_get_drvdata(wdt);
+
+ return bcma_chipco_watchdog_timer_set(cc, ticks);
+}
+
+static u32 bcma_chipco_watchdog_timer_set_ms_wdt(struct bcm47xx_wdt *wdt,
+ u32 ms)
+{
+ struct bcma_drv_cc *cc = bcm47xx_wdt_get_drvdata(wdt);
+ u32 ticks;
+
+ ticks = bcma_chipco_watchdog_timer_set(cc, cc->ticks_per_ms * ms);
+ return ticks / cc->ticks_per_ms;
+}
+
+static int bcma_chipco_watchdog_ticks_per_ms(struct bcma_drv_cc *cc)
+{
+ struct bcma_bus *bus = cc->core->bus;
+
+ if (cc->capabilities & BCMA_CC_CAP_PMU) {
+ if (bus->chipinfo.id == BCMA_CHIP_ID_BCM4706)
+ /* 4706 CC and PMU watchdogs are clocked at 1/4 of ALP clock */
+ return bcma_chipco_alp_clock(cc) / 4000;
+ else
+ /* based on 32KHz ILP clock */
+ return 32;
+ } else {
+ return bcma_chipco_alp_clock(cc) / 1000;
+ }
+}
void bcma_core_chipcommon_early_init(struct bcma_drv_cc *cc)
{
@@ -100,12 +134,13 @@ void bcma_core_chipcommon_init(struct bcma_drv_cc *cc)
((leddc_on << BCMA_CC_GPIOTIMER_ONTIME_SHIFT) |
(leddc_off << BCMA_CC_GPIOTIMER_OFFTIME_SHIFT)));
}
+ cc->ticks_per_ms = bcma_chipco_watchdog_ticks_per_ms(cc);
cc->setup_done = true;
}
/* Set chip watchdog reset timer to fire in 'ticks' backplane cycles */
-void bcma_chipco_watchdog_timer_set(struct bcma_drv_cc *cc, u32 ticks)
+u32 bcma_chipco_watchdog_timer_set(struct bcma_drv_cc *cc, u32 ticks)
{
u32 maxt;
enum bcma_clkmode clkmode;
@@ -125,6 +160,7 @@ void bcma_chipco_watchdog_timer_set(struct bcma_drv_cc *cc, u32 ticks)
/* instant NMI */
bcma_cc_write32(cc, BCMA_CC_WATCHDOG, ticks);
}
+ return ticks;
}
void bcma_chipco_irq_mask(struct bcma_drv_cc *cc, u32 mask, u32 value)
diff --git a/include/linux/bcma/bcma_driver_chipcommon.h b/include/linux/bcma/bcma_driver_chipcommon.h
index 145f3c5..2f9b014 100644
--- a/include/linux/bcma/bcma_driver_chipcommon.h
+++ b/include/linux/bcma/bcma_driver_chipcommon.h
@@ -570,6 +570,7 @@ struct bcma_drv_cc {
int nr_serial_ports;
struct bcma_serial_port serial_ports[4];
#endif /* CONFIG_BCMA_DRIVER_MIPS */
+ u32 ticks_per_ms;
};
/* Register access */
@@ -593,8 +594,7 @@ extern void bcma_chipco_resume(struct bcma_drv_cc *cc);
void bcma_chipco_bcm4331_ext_pa_lines_ctl(struct bcma_drv_cc *cc, bool enable);
-extern void bcma_chipco_watchdog_timer_set(struct bcma_drv_cc *cc,
- u32 ticks);
+extern u32 bcma_chipco_watchdog_timer_set(struct bcma_drv_cc *cc, u32 ticks);
void bcma_chipco_irq_mask(struct bcma_drv_cc *cc, u32 mask, u32 value);
--
1.7.10.4
Register the watchdog driver to the system if this is a SoC. Using the
watchdog on a non SoC device, like a PCIe card, it will make the PCIe
card die when the timeout expired, but starting it again is not
supported.
Signed-off-by: Hauke Mehrtens <[email protected]>
---
drivers/ssb/embedded.c | 35 +++++++++++++++++++++++++++++++++++
drivers/ssb/main.c | 8 ++++++++
drivers/ssb/ssb_private.h | 10 ++++++++++
include/linux/ssb/ssb.h | 2 ++
4 files changed, 55 insertions(+)
diff --git a/drivers/ssb/embedded.c b/drivers/ssb/embedded.c
index 9ef124f..bb18d76 100644
--- a/drivers/ssb/embedded.c
+++ b/drivers/ssb/embedded.c
@@ -4,11 +4,13 @@
*
* Copyright 2005-2008, Broadcom Corporation
* Copyright 2006-2008, Michael Buesch <[email protected]>
+ * Copyright 2012, Hauke Mehrtens <[email protected]>
*
* Licensed under the GNU/GPL. See COPYING for details.
*/
#include <linux/export.h>
+#include <linux/platform_device.h>
#include <linux/ssb/ssb.h>
#include <linux/ssb/ssb_embedded.h>
#include <linux/ssb/ssb_driver_pci.h>
@@ -32,6 +34,39 @@ int ssb_watchdog_timer_set(struct ssb_bus *bus, u32 ticks)
}
EXPORT_SYMBOL(ssb_watchdog_timer_set);
+int ssb_watchdog_register(struct ssb_bus *bus)
+{
+ struct bcm47xx_wdt wdt = {};
+ struct platform_device *pdev;
+
+ if (ssb_chipco_available(&bus->chipco)) {
+ wdt.driver_data = &bus->chipco;
+ wdt.timer_set = ssb_chipco_watchdog_timer_set_wdt;
+ wdt.timer_set_ms = ssb_chipco_watchdog_timer_set_ms;
+ wdt.max_timer_ms = bus->chipco.max_timer_ms;
+ } else if (ssb_extif_available(&bus->extif)) {
+ wdt.driver_data = &bus->extif;
+ wdt.timer_set = ssb_extif_watchdog_timer_set_wdt;
+ wdt.timer_set_ms = ssb_extif_watchdog_timer_set_ms;
+ wdt.max_timer_ms = SSB_EXTIF_WATCHDOG_MAX_TIMER_MS;
+ } else {
+ return -ENODEV;
+ }
+
+ pdev = platform_device_register_data(NULL, "bcm47xx-wdt",
+ bus->busnumber, &wdt,
+ sizeof(wdt));
+ if (IS_ERR(pdev)) {
+ ssb_dprintk(KERN_INFO PFX
+ "can not register watchdog device, err: %li\n",
+ PTR_ERR(pdev));
+ return PTR_ERR(pdev);
+ }
+
+ bus->watchdog = pdev;
+ return 0;
+}
+
u32 ssb_gpio_in(struct ssb_bus *bus, u32 mask)
{
unsigned long flags;
diff --git a/drivers/ssb/main.c b/drivers/ssb/main.c
index df0f145..58c7da2 100644
--- a/drivers/ssb/main.c
+++ b/drivers/ssb/main.c
@@ -13,6 +13,7 @@
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/module.h>
+#include <linux/platform_device.h>
#include <linux/ssb/ssb.h>
#include <linux/ssb/ssb_regs.h>
#include <linux/ssb/ssb_driver_gige.h>
@@ -433,6 +434,11 @@ static void ssb_devices_unregister(struct ssb_bus *bus)
if (sdev->dev)
device_unregister(sdev->dev);
}
+
+#ifdef CONFIG_SSB_EMBEDDED
+ if (bus->bustype == SSB_BUSTYPE_SSB)
+ platform_device_unregister(bus->watchdog);
+#endif
}
void ssb_bus_unregister(struct ssb_bus *bus)
@@ -561,6 +567,8 @@ static int __devinit ssb_attach_queued_buses(void)
if (err)
goto error;
ssb_pcicore_init(&bus->pcicore);
+ if (bus->bustype == SSB_BUSTYPE_SSB)
+ ssb_watchdog_register(bus);
ssb_bus_may_powerdown(bus);
err = ssb_devices_register(bus);
diff --git a/drivers/ssb/ssb_private.h b/drivers/ssb/ssb_private.h
index 50ea028..8942db1 100644
--- a/drivers/ssb/ssb_private.h
+++ b/drivers/ssb/ssb_private.h
@@ -232,4 +232,14 @@ static inline u32 ssb_extif_watchdog_timer_set_ms(struct bcm47xx_wdt *wdt,
return 0;
}
#endif
+
+#ifdef CONFIG_SSB_EMBEDDED
+extern int ssb_watchdog_register(struct ssb_bus *bus);
+#else /* CONFIG_SSB_EMBEDDED */
+static inline int ssb_watchdog_register(struct ssb_bus *bus)
+{
+ return 0;
+}
+#endif /* CONFIG_SSB_EMBEDDED */
+
#endif /* LINUX_SSB_PRIVATE_H_ */
diff --git a/include/linux/ssb/ssb.h b/include/linux/ssb/ssb.h
index bb674c0..1f64e3f 100644
--- a/include/linux/ssb/ssb.h
+++ b/include/linux/ssb/ssb.h
@@ -8,6 +8,7 @@
#include <linux/pci.h>
#include <linux/mod_devicetable.h>
#include <linux/dma-mapping.h>
+#include <linux/platform_device.h>
#include <linux/ssb/ssb_regs.h>
@@ -432,6 +433,7 @@ struct ssb_bus {
#ifdef CONFIG_SSB_EMBEDDED
/* Lock for GPIO register access. */
spinlock_t gpio_lock;
+ struct platform_device *watchdog;
#endif /* EMBEDDED */
/* Internal-only stuff follows. Do not touch. */
--
1.7.10.4
The watchdog driver wants to set the watchdog timeout in ms and not in
ticks, which is depending on the SoC type and the clock. Return the
ticks or millisecond the timer was set to in case the provided value
was bigger than the max allowed value.
Signed-off-by: Hauke Mehrtens <[email protected]>
---
drivers/ssb/driver_extif.c | 21 ++++++++++++++++++++-
drivers/ssb/ssb_private.h | 15 +++++++++++++++
include/linux/ssb/ssb_driver_extif.h | 9 +++++----
3 files changed, 40 insertions(+), 5 deletions(-)
diff --git a/drivers/ssb/driver_extif.c b/drivers/ssb/driver_extif.c
index 0aa4c2a..553227a 100644
--- a/drivers/ssb/driver_extif.c
+++ b/drivers/ssb/driver_extif.c
@@ -112,11 +112,30 @@ void ssb_extif_get_clockcontrol(struct ssb_extif *extif,
*m = extif_read32(extif, SSB_EXTIF_CLOCK_SB);
}
-void ssb_extif_watchdog_timer_set(struct ssb_extif *extif, u32 ticks)
+u32 ssb_extif_watchdog_timer_set_wdt(struct bcm47xx_wdt *wdt, u32 ticks)
+{
+ struct ssb_extif *extif = bcm47xx_wdt_get_drvdata(wdt);
+
+ return ssb_extif_watchdog_timer_set(extif, ticks);
+}
+
+u32 ssb_extif_watchdog_timer_set_ms(struct bcm47xx_wdt *wdt, u32 ms)
+{
+ struct ssb_extif *extif = bcm47xx_wdt_get_drvdata(wdt);
+ u32 ticks = (SSB_EXTIF_WATCHDOG_CLK / 1000) * ms;
+
+ ticks = ssb_extif_watchdog_timer_set(extif, ticks);
+
+ return (ticks * 1000) / SSB_EXTIF_WATCHDOG_CLK;
+}
+
+u32 ssb_extif_watchdog_timer_set(struct ssb_extif *extif, u32 ticks)
{
if (ticks > SSB_EXTIF_WATCHDOG_MAX_TIMER)
ticks = SSB_EXTIF_WATCHDOG_MAX_TIMER;
extif_write32(extif, SSB_EXTIF_WATCHDOG, ticks);
+
+ return ticks;
}
u32 ssb_extif_gpio_in(struct ssb_extif *extif, u32 mask)
diff --git a/drivers/ssb/ssb_private.h b/drivers/ssb/ssb_private.h
index 03cc40a..50ea028 100644
--- a/drivers/ssb/ssb_private.h
+++ b/drivers/ssb/ssb_private.h
@@ -217,4 +217,19 @@ extern u32 ssb_chipco_watchdog_timer_set_wdt(struct bcm47xx_wdt *wdt,
u32 ticks);
extern u32 ssb_chipco_watchdog_timer_set_ms(struct bcm47xx_wdt *wdt, u32 ms);
+#ifdef CONFIG_SSB_DRIVER_EXTIF
+extern u32 ssb_extif_watchdog_timer_set_wdt(struct bcm47xx_wdt *wdt, u32 ticks);
+extern u32 ssb_extif_watchdog_timer_set_ms(struct bcm47xx_wdt *wdt, u32 ms);
+#else
+static inline u32 ssb_extif_watchdog_timer_set_wdt(struct bcm47xx_wdt *wdt,
+ u32 ticks)
+{
+ return 0;
+}
+static inline u32 ssb_extif_watchdog_timer_set_ms(struct bcm47xx_wdt *wdt,
+ u32 ms)
+{
+ return 0;
+}
+#endif
#endif /* LINUX_SSB_PRIVATE_H_ */
diff --git a/include/linux/ssb/ssb_driver_extif.h b/include/linux/ssb/ssb_driver_extif.h
index 34072f5..42dd04b 100644
--- a/include/linux/ssb/ssb_driver_extif.h
+++ b/include/linux/ssb/ssb_driver_extif.h
@@ -153,6 +153,8 @@
#define SSB_EXTIF_WATCHDOG_CLK 48000000 /* Hz */
#define SSB_EXTIF_WATCHDOG_MAX_TIMER ((1 << 28) - 1)
+#define SSB_EXTIF_WATCHDOG_MAX_TIMER_MS (SSB_EXTIF_WATCHDOG_MAX_TIMER \
+ / (SSB_EXTIF_WATCHDOG_CLK / 1000))
#ifdef CONFIG_SSB_DRIVER_EXTIF
@@ -172,8 +174,7 @@ extern void ssb_extif_get_clockcontrol(struct ssb_extif *extif,
extern void ssb_extif_timing_init(struct ssb_extif *extif,
unsigned long ns);
-extern void ssb_extif_watchdog_timer_set(struct ssb_extif *extif,
- u32 ticks);
+extern u32 ssb_extif_watchdog_timer_set(struct ssb_extif *extif, u32 ticks);
/* Extif GPIO pin access */
u32 ssb_extif_gpio_in(struct ssb_extif *extif, u32 mask);
@@ -206,9 +207,9 @@ void ssb_extif_get_clockcontrol(struct ssb_extif *extif,
}
static inline
-void ssb_extif_watchdog_timer_set(struct ssb_extif *extif,
- u32 ticks)
+u32 ssb_extif_watchdog_timer_set(struct ssb_extif *extif, u32 ticks)
{
+ return 0;
}
#endif /* CONFIG_SSB_DRIVER_EXTIF */
--
1.7.10.4
On 11/24/2012 11:36 PM, Sergei Shtylyov wrote:
> Hello.
>
> On 25-11-2012 2:24, Hauke Mehrtens wrote:
>
>> Rename rename
>
> Once it enough. :-)
>
>> wdt_timeout
>
> 'wdt_time', you mean?
>
>> to timeout to name it like the other watchdog
>> driver do it.
>
> It's not the only change you're doing.
>
Thanks for spotting this, I will change the changelog.
Hauke
Register the watchdog driver to the system if this is a SoC. Using the
watchdog on a non SoC device, like a PCIe card, it will make the PCIe
card die when the timeout expired, but starting it again is not
supported.
Signed-off-by: Hauke Mehrtens <[email protected]>
---
drivers/bcma/bcma_private.h | 2 ++
drivers/bcma/driver_chipcommon.c | 22 ++++++++++++++++++++++
drivers/bcma/main.c | 8 ++++++++
include/linux/bcma/bcma_driver_chipcommon.h | 3 +++
4 files changed, 35 insertions(+)
diff --git a/drivers/bcma/bcma_private.h b/drivers/bcma/bcma_private.h
index 169fc58..bcb830e 100644
--- a/drivers/bcma/bcma_private.h
+++ b/drivers/bcma/bcma_private.h
@@ -84,6 +84,8 @@ extern void __exit bcma_host_pci_exit(void);
/* driver_pci.c */
u32 bcma_pcie_read(struct bcma_drv_pci *pc, u32 address);
+extern int bcma_chipco_watchdog_register(struct bcma_drv_cc *cc);
+
#ifdef CONFIG_BCMA_DRIVER_PCI_HOSTMODE
bool __devinit bcma_core_pci_is_in_hostmode(struct bcma_drv_pci *pc);
void __devinit bcma_core_pci_hostmode_init(struct bcma_drv_pci *pc);
diff --git a/drivers/bcma/driver_chipcommon.c b/drivers/bcma/driver_chipcommon.c
index 1172226..d017f25 100644
--- a/drivers/bcma/driver_chipcommon.c
+++ b/drivers/bcma/driver_chipcommon.c
@@ -12,6 +12,7 @@
#include "bcma_private.h"
#include <linux/bcm47xx_wdt.h>
#include <linux/export.h>
+#include <linux/platform_device.h>
#include <linux/bcma/bcma.h>
static inline u32 bcma_cc_write32_masked(struct bcma_drv_cc *cc, u16 offset,
@@ -87,6 +88,27 @@ static int bcma_chipco_watchdog_ticks_per_ms(struct bcma_drv_cc *cc)
}
}
+int bcma_chipco_watchdog_register(struct bcma_drv_cc *cc)
+{
+ struct bcm47xx_wdt wdt = {};
+ struct platform_device *pdev;
+
+ wdt.driver_data = cc;
+ wdt.timer_set = bcma_chipco_watchdog_timer_set_wdt;
+ wdt.timer_set_ms = bcma_chipco_watchdog_timer_set_ms_wdt;
+ wdt.max_timer_ms = bcma_chipco_watchdog_get_max_timer(cc) / cc->ticks_per_ms;
+
+ pdev = platform_device_register_data(NULL, "bcm47xx-wdt",
+ cc->core->bus->num, &wdt,
+ sizeof(wdt));
+ if (IS_ERR(pdev))
+ return PTR_ERR(pdev);
+
+ cc->watchdog = pdev;
+
+ return 0;
+}
+
void bcma_core_chipcommon_early_init(struct bcma_drv_cc *cc)
{
if (cc->early_setup_done)
diff --git a/drivers/bcma/main.c b/drivers/bcma/main.c
index a971889..debd4f1 100644
--- a/drivers/bcma/main.c
+++ b/drivers/bcma/main.c
@@ -165,6 +165,12 @@ static int bcma_register_cores(struct bcma_bus *bus)
}
#endif
+ if (bus->hosttype == BCMA_HOSTTYPE_SOC) {
+ err = bcma_chipco_watchdog_register(&bus->drv_cc);
+ if (err)
+ bcma_err(bus, "Error registering watchdog driver\n");
+ }
+
return 0;
}
@@ -177,6 +183,8 @@ static void bcma_unregister_cores(struct bcma_bus *bus)
if (core->dev_registered)
device_unregister(&core->dev);
}
+ if (bus->hosttype == BCMA_HOSTTYPE_SOC)
+ platform_device_unregister(bus->drv_cc.watchdog);
}
int __devinit bcma_bus_register(struct bcma_bus *bus)
diff --git a/include/linux/bcma/bcma_driver_chipcommon.h b/include/linux/bcma/bcma_driver_chipcommon.h
index 2f9b014..e513591 100644
--- a/include/linux/bcma/bcma_driver_chipcommon.h
+++ b/include/linux/bcma/bcma_driver_chipcommon.h
@@ -1,6 +1,8 @@
#ifndef LINUX_BCMA_DRIVER_CC_H_
#define LINUX_BCMA_DRIVER_CC_H_
+#include <linux/platform_device.h>
+
/** ChipCommon core registers. **/
#define BCMA_CC_ID 0x0000
#define BCMA_CC_ID_ID 0x0000FFFF
@@ -571,6 +573,7 @@ struct bcma_drv_cc {
struct bcma_serial_port serial_ports[4];
#endif /* CONFIG_BCMA_DRIVER_MIPS */
u32 ticks_per_ms;
+ struct platform_device *watchdog;
};
/* Register access */
--
1.7.10.4
Rename rename wdt_timeout to timeout to name it like the other watchdog
driver do it.
Signed-off-by: Hauke Mehrtens <[email protected]>
---
drivers/watchdog/bcm47xx_wdt.c | 14 +++++++-------
1 file changed, 7 insertions(+), 7 deletions(-)
diff --git a/drivers/watchdog/bcm47xx_wdt.c b/drivers/watchdog/bcm47xx_wdt.c
index 66f2d2b..b6a8c49 100644
--- a/drivers/watchdog/bcm47xx_wdt.c
+++ b/drivers/watchdog/bcm47xx_wdt.c
@@ -30,13 +30,13 @@
#define DRV_NAME "bcm47xx_wdt"
#define WDT_DEFAULT_TIME 30 /* seconds */
-#define WDT_MAX_TIME 255 /* seconds */
+#define WDT_SOFTTIMER_MAX 3600 /* seconds */
-static int wdt_time = WDT_DEFAULT_TIME;
+static int timeout = WDT_DEFAULT_TIME;
static bool nowayout = WATCHDOG_NOWAYOUT;
-module_param(wdt_time, int, 0);
-MODULE_PARM_DESC(wdt_time, "Watchdog time in seconds. (default="
+module_param(timeout, int, 0);
+MODULE_PARM_DESC(timeout, "Watchdog time in seconds. (default="
__MODULE_STRING(WDT_DEFAULT_TIME) ")");
module_param(nowayout, bool, 0);
@@ -94,9 +94,9 @@ static int bcm47xx_wdt_soft_stop(struct watchdog_device *wdd)
static int bcm47xx_wdt_soft_set_timeout(struct watchdog_device *wdd,
unsigned int new_time)
{
- if (new_time < 1 || new_time > WDT_MAX_TIME) {
+ if (new_time < 1 || new_time > WDT_SOFTTIMER_MAX) {
pr_warn("timeout value must be 1<=x<=%d, using %d\n",
- WDT_MAX_TIME, new_time);
+ WDT_SOFTTIMER_MAX, new_time);
return -EINVAL;
}
@@ -160,7 +160,7 @@ static int __devinit bcm47xx_wdt_probe(struct platform_device *pdev)
goto err_notifier;
pr_info("BCM47xx Watchdog Timer enabled (%d seconds%s)\n",
- wdt_time, nowayout ? ", nowayout" : "");
+ timeout, nowayout ? ", nowayout" : "");
return 0;
err_notifier:
--
1.7.10.4
Hello.
On 25-11-2012 2:24, Hauke Mehrtens wrote:
> Rename rename
Once it enough. :-)
> wdt_timeout
'wdt_time', you mean?
> to timeout to name it like the other watchdog
> driver do it.
It's not the only change you're doing.
> Signed-off-by: Hauke Mehrtens <[email protected]>
> ---
> drivers/watchdog/bcm47xx_wdt.c | 14 +++++++-------
> 1 file changed, 7 insertions(+), 7 deletions(-)
> diff --git a/drivers/watchdog/bcm47xx_wdt.c b/drivers/watchdog/bcm47xx_wdt.c
> index 66f2d2b..b6a8c49 100644
> --- a/drivers/watchdog/bcm47xx_wdt.c
> +++ b/drivers/watchdog/bcm47xx_wdt.c
> @@ -30,13 +30,13 @@
> #define DRV_NAME "bcm47xx_wdt"
>
> #define WDT_DEFAULT_TIME 30 /* seconds */
> -#define WDT_MAX_TIME 255 /* seconds */
> +#define WDT_SOFTTIMER_MAX 3600 /* seconds */
>
> -static int wdt_time = WDT_DEFAULT_TIME;
> +static int timeout = WDT_DEFAULT_TIME;
> static bool nowayout = WATCHDOG_NOWAYOUT;
>
> -module_param(wdt_time, int, 0);
> -MODULE_PARM_DESC(wdt_time, "Watchdog time in seconds. (default="
> +module_param(timeout, int, 0);
> +MODULE_PARM_DESC(timeout, "Watchdog time in seconds. (default="
> __MODULE_STRING(WDT_DEFAULT_TIME) ")");
>
> module_param(nowayout, bool, 0);
> @@ -94,9 +94,9 @@ static int bcm47xx_wdt_soft_stop(struct watchdog_device *wdd)
> static int bcm47xx_wdt_soft_set_timeout(struct watchdog_device *wdd,
> unsigned int new_time)
> {
> - if (new_time < 1 || new_time > WDT_MAX_TIME) {
> + if (new_time < 1 || new_time > WDT_SOFTTIMER_MAX) {
> pr_warn("timeout value must be 1<=x<=%d, using %d\n",
> - WDT_MAX_TIME, new_time);
> + WDT_SOFTTIMER_MAX, new_time);
> return -EINVAL;
> }
>
WBR, Sergei
Instead of accessing the function to set the watchdog timer directly
register a platform driver the platform could register to use this
watchdog driver.
Signed-off-by: Hauke Mehrtens <[email protected]>
---
drivers/watchdog/bcm47xx_wdt.c | 157 ++++++++++++++++++++--------------------
include/linux/bcm47xx_wdt.h | 27 +++++++
2 files changed, 105 insertions(+), 79 deletions(-)
create mode 100644 include/linux/bcm47xx_wdt.h
diff --git a/drivers/watchdog/bcm47xx_wdt.c b/drivers/watchdog/bcm47xx_wdt.c
index a527175..cf1191b 100644
--- a/drivers/watchdog/bcm47xx_wdt.c
+++ b/drivers/watchdog/bcm47xx_wdt.c
@@ -3,6 +3,7 @@
*
* Copyright (C) 2008 Aleksandar Radovanovic <[email protected]>
* Copyright (C) 2009 Matthieu CASTET <[email protected]>
+ * Copyright (C) 2012 Hauke Mehrtens <[email protected]>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -12,19 +13,19 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+#include <linux/bcm47xx_wdt.h>
#include <linux/bitops.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
+#include <linux/platform_device.h>
#include <linux/reboot.h>
#include <linux/types.h>
#include <linux/watchdog.h>
#include <linux/timer.h>
#include <linux/jiffies.h>
-#include <linux/ssb/ssb_embedded.h>
-#include <asm/mach-bcm47xx/bcm47xx.h>
#define DRV_NAME "bcm47xx_wdt"
@@ -43,48 +44,19 @@ MODULE_PARM_DESC(nowayout,
"Watchdog cannot be stopped once started (default="
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
-static struct timer_list wdt_timer;
-static atomic_t ticks;
-
-static inline void bcm47xx_wdt_hw_start(void)
+static inline struct bcm47xx_wdt *bcm47xx_wdt_get(struct watchdog_device *wdd)
{
- /* this is 2,5s on 100Mhz clock and 2s on 133 Mhz */
- switch (bcm47xx_bus_type) {
-#ifdef CONFIG_BCM47XX_SSB
- case BCM47XX_BUS_TYPE_SSB:
- ssb_watchdog_timer_set(&bcm47xx_bus.ssb, 0xfffffff);
- break;
-#endif
-#ifdef CONFIG_BCM47XX_BCMA
- case BCM47XX_BUS_TYPE_BCMA:
- bcma_chipco_watchdog_timer_set(&bcm47xx_bus.bcma.bus.drv_cc,
- 0xfffffff);
- break;
-#endif
- }
+ return container_of(wdd, struct bcm47xx_wdt, wdd);
}
-static inline int bcm47xx_wdt_hw_stop(void)
+static void bcm47xx_timer_tick(unsigned long data)
{
- switch (bcm47xx_bus_type) {
-#ifdef CONFIG_BCM47XX_SSB
- case BCM47XX_BUS_TYPE_SSB:
- return ssb_watchdog_timer_set(&bcm47xx_bus.ssb, 0);
-#endif
-#ifdef CONFIG_BCM47XX_BCMA
- case BCM47XX_BUS_TYPE_BCMA:
- bcma_chipco_watchdog_timer_set(&bcm47xx_bus.bcma.bus.drv_cc, 0);
- return 0;
-#endif
- }
- return -EINVAL;
-}
+ struct bcm47xx_wdt *wdt = (struct bcm47xx_wdt *)data;
+ u32 next_tick = min(wdt->wdd.timeout * 1000, wdt->max_timer_ms);
-static void bcm47xx_timer_tick(unsigned long unused)
-{
- if (!atomic_dec_and_test(&ticks)) {
- bcm47xx_wdt_hw_start();
- mod_timer(&wdt_timer, jiffies + HZ);
+ if (!atomic_dec_and_test(&wdt->soft_ticks)) {
+ wdt->timer_set_ms(wdt, next_tick);
+ mod_timer(&wdt->soft_timer, jiffies + HZ);
} else {
pr_crit("Watchdog will fire soon!!!\n");
}
@@ -92,23 +64,29 @@ static void bcm47xx_timer_tick(unsigned long unused)
static int bcm47xx_wdt_keepalive(struct watchdog_device *wdd)
{
- atomic_set(&ticks, wdt_time);
+ struct bcm47xx_wdt *wdt = bcm47xx_wdt_get(wdd);
+
+ atomic_set(&wdt->soft_ticks, wdd->timeout);
return 0;
}
static int bcm47xx_wdt_start(struct watchdog_device *wdd)
{
- bcm47xx_wdt_pet();
- bcm47xx_timer_tick(0);
+ struct bcm47xx_wdt *wdt = bcm47xx_wdt_get(wdd);
+
+ bcm47xx_wdt_keepalive(wdd);
+ bcm47xx_timer_tick((unsigned long)wdt);
return 0;
}
static int bcm47xx_wdt_stop(struct watchdog_device *wdd)
{
- del_timer_sync(&wdt_timer);
- bcm47xx_wdt_hw_stop();
+ struct bcm47xx_wdt *wdt = bcm47xx_wdt_get(wdd);
+
+ del_timer_sync(&wdt->soft_timer);
+ wdt->timer_set(wdt, 0);
return 0;
}
@@ -116,10 +94,13 @@ static int bcm47xx_wdt_stop(struct watchdog_device *wdd)
static int bcm47xx_wdt_set_timeout(struct watchdog_device *wdd,
unsigned int new_time)
{
- if ((new_time <= 0) || (new_time > WDT_MAX_TIME))
+ if (new_time < 1 || new_time > WDT_MAX_TIME) {
+ pr_warn("timeout value must be 1<=x<=%d, using %d\n",
+ WDT_MAX_TIME, new_time);
return -EINVAL;
+ }
- wdt_time = new_time;
+ wdd->timeout = new_time;
return 0;
}
@@ -133,8 +114,11 @@ static const struct watchdog_info bcm47xx_wdt_info = {
static int bcm47xx_wdt_notify_sys(struct notifier_block *this,
unsigned long code, void *unused)
{
+ struct bcm47xx_wdt *wdt;
+
+ wdt = container_of(this, struct bcm47xx_wdt, notifier);
if (code == SYS_DOWN || code == SYS_HALT)
- bcm47xx_wdt_stop();
+ wdt->wdd.ops->stop(&wdt->wdd);
return NOTIFY_DONE;
}
@@ -146,57 +130,72 @@ static struct watchdog_ops bcm47xx_wdt_ops = {
.set_timeout = bcm47xx_wdt_set_timeout,
};
-static struct watchdog_device bcm47xx_wdt_wdd = {
- .info = &bcm47xx_wdt_info,
- .ops = &bcm47xx_wdt_ops,
-};
-
-static struct notifier_block bcm47xx_wdt_notifier = {
- .notifier_call = bcm47xx_wdt_notify_sys,
-};
-
-static int __init bcm47xx_wdt_init(void)
+static int __devinit bcm47xx_wdt_probe(struct platform_device *pdev)
{
int ret;
+ struct bcm47xx_wdt *wdt = dev_get_platdata(&pdev->dev);
- if (bcm47xx_wdt_hw_stop() < 0)
- return -ENODEV;
+ if (!wdt)
+ return -ENXIO;
- setup_timer(&wdt_timer, bcm47xx_timer_tick, 0L);
+ setup_timer(&wdt->soft_timer, bcm47xx_timer_tick,
+ (long unsigned int)wdt);
- if (bcm47xx_wdt_settimeout(wdt_time)) {
- bcm47xx_wdt_settimeout(WDT_DEFAULT_TIME);
- pr_info("wdt_time value must be 0 < wdt_time < %d, using %d\n",
- (WDT_MAX_TIME + 1), wdt_time);
- }
- watchdog_set_nowayout(&bcm47xx_wdt_wdd, nowayout);
+ wdt->wdd.ops = &bcm47xx_wdt_ops;
+ wdt->wdd.info = &bcm47xx_wdt_info;
+ wdt->wdd.timeout = WDT_DEFAULT_TIME;
+ ret = wdt->wdd.ops->set_timeout(&wdt->wdd, timeout);
+ if (ret)
+ goto err_timer;
+ watchdog_set_nowayout(&wdt->wdd, nowayout);
+
+ wdt->notifier.notifier_call = &bcm47xx_wdt_notify_sys;
- ret = register_reboot_notifier(&bcm47xx_wdt_notifier);
+ ret = register_reboot_notifier(&wdt->notifier);
if (ret)
- return ret;
+ goto err_timer;
- ret = watchdog_register_device(&bcm47xx_wdt_wdd);
- if (ret) {
- unregister_reboot_notifier(&bcm47xx_wdt_notifier);
- return ret;
- }
+ ret = watchdog_register_device(&wdt->wdd);
+ if (ret)
+ goto err_notifier;
pr_info("BCM47xx Watchdog Timer enabled (%d seconds%s)\n",
wdt_time, nowayout ? ", nowayout" : "");
return 0;
+
+err_notifier:
+ unregister_reboot_notifier(&wdt->notifier);
+err_timer:
+ del_timer_sync(&wdt->soft_timer);
+
+ return ret;
}
-static void __exit bcm47xx_wdt_exit(void)
+static int __devexit bcm47xx_wdt_remove(struct platform_device *pdev)
{
- watchdog_stop(&bcm47xx_wdt_wdd);
- watchdog_unregister_device(&bcm47xx_wdt_wdd);
+ struct bcm47xx_wdt *wdt = dev_get_platdata(&pdev->dev);
+
+ if (!wdt)
+ return -ENXIO;
+
+ watchdog_unregister_device(&wdt->wdd);
+ unregister_reboot_notifier(&wdt->notifier);
- unregister_reboot_notifier(&bcm47xx_wdt_notifier);
+ return 0;
}
-module_init(bcm47xx_wdt_init);
-module_exit(bcm47xx_wdt_exit);
+static struct platform_driver bcm47xx_wdt_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "bcm47xx-wdt",
+ },
+ .probe = bcm47xx_wdt_probe,
+ .remove = __devexit_p(bcm47xx_wdt_remove),
+};
+
+module_platform_driver(bcm47xx_wdt_driver);
MODULE_AUTHOR("Aleksandar Radovanovic");
+MODULE_AUTHOR("Hauke Mehrtens <[email protected]>");
MODULE_DESCRIPTION("Watchdog driver for Broadcom BCM47xx");
MODULE_LICENSE("GPL");
diff --git a/include/linux/bcm47xx_wdt.h b/include/linux/bcm47xx_wdt.h
new file mode 100644
index 0000000..b94ac0e
--- /dev/null
+++ b/include/linux/bcm47xx_wdt.h
@@ -0,0 +1,27 @@
+#ifndef LINUX_BCM47XX_WDT_H_
+#define LINUX_BCM47XX_WDT_H_
+
+#include <linux/notifier.h>
+#include <linux/timer.h>
+#include <linux/types.h>
+#include <linux/watchdog.h>
+
+
+struct bcm47xx_wdt {
+ u32 (*timer_set)(struct bcm47xx_wdt *, u32);
+ u32 (*timer_set_ms)(struct bcm47xx_wdt *, u32);
+
+ void *driver_data;
+
+ struct watchdog_device wdd;
+ struct notifier_block notifier;
+
+ struct timer_list soft_timer;
+ atomic_t soft_ticks;
+};
+
+static inline void *bcm47xx_wdt_get_drvdata(struct bcm47xx_wdt *wdt)
+{
+ return wdt->driver_data;
+}
+#endif /* LINUX_BCM47XX_WDT_H_ */
--
1.7.10.4
Convert the bcm47xx_wdt.c driver to the new watchdog core api.
Signed-off-by: Hauke Mehrtens <[email protected]>
---
drivers/watchdog/Kconfig | 1 +
drivers/watchdog/bcm47xx_wdt.c | 153 ++++++----------------------------------
2 files changed, 24 insertions(+), 130 deletions(-)
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index ad1bb93..9f21356 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -959,6 +959,7 @@ config ATH79_WDT
config BCM47XX_WDT
tristate "Broadcom BCM47xx Watchdog Timer"
depends on BCM47XX
+ select WATCHDOG_CORE
help
Hardware driver for the Broadcom BCM47xx Watchdog Timer.
diff --git a/drivers/watchdog/bcm47xx_wdt.c b/drivers/watchdog/bcm47xx_wdt.c
index bc0e91e..a527175 100644
--- a/drivers/watchdog/bcm47xx_wdt.c
+++ b/drivers/watchdog/bcm47xx_wdt.c
@@ -14,15 +14,12 @@
#include <linux/bitops.h>
#include <linux/errno.h>
-#include <linux/fs.h>
#include <linux/init.h>
#include <linux/kernel.h>
-#include <linux/miscdevice.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/reboot.h>
#include <linux/types.h>
-#include <linux/uaccess.h>
#include <linux/watchdog.h>
#include <linux/timer.h>
#include <linux/jiffies.h>
@@ -41,15 +38,11 @@ module_param(wdt_time, int, 0);
MODULE_PARM_DESC(wdt_time, "Watchdog time in seconds. (default="
__MODULE_STRING(WDT_DEFAULT_TIME) ")");
-#ifdef CONFIG_WATCHDOG_NOWAYOUT
module_param(nowayout, bool, 0);
MODULE_PARM_DESC(nowayout,
"Watchdog cannot be stopped once started (default="
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
-#endif
-static unsigned long bcm47xx_wdt_busy;
-static char expect_release;
static struct timer_list wdt_timer;
static atomic_t ticks;
@@ -97,29 +90,31 @@ static void bcm47xx_timer_tick(unsigned long unused)
}
}
-static inline void bcm47xx_wdt_pet(void)
+static int bcm47xx_wdt_keepalive(struct watchdog_device *wdd)
{
atomic_set(&ticks, wdt_time);
+
+ return 0;
}
-static void bcm47xx_wdt_start(void)
+static int bcm47xx_wdt_start(struct watchdog_device *wdd)
{
bcm47xx_wdt_pet();
bcm47xx_timer_tick(0);
+
+ return 0;
}
-static void bcm47xx_wdt_pause(void)
+static int bcm47xx_wdt_stop(struct watchdog_device *wdd)
{
del_timer_sync(&wdt_timer);
bcm47xx_wdt_hw_stop();
-}
-static void bcm47xx_wdt_stop(void)
-{
- bcm47xx_wdt_pause();
+ return 0;
}
-static int bcm47xx_wdt_settimeout(int new_time)
+static int bcm47xx_wdt_set_timeout(struct watchdog_device *wdd,
+ unsigned int new_time)
{
if ((new_time <= 0) || (new_time > WDT_MAX_TIME))
return -EINVAL;
@@ -128,51 +123,6 @@ static int bcm47xx_wdt_settimeout(int new_time)
return 0;
}
-static int bcm47xx_wdt_open(struct inode *inode, struct file *file)
-{
- if (test_and_set_bit(0, &bcm47xx_wdt_busy))
- return -EBUSY;
-
- bcm47xx_wdt_start();
- return nonseekable_open(inode, file);
-}
-
-static int bcm47xx_wdt_release(struct inode *inode, struct file *file)
-{
- if (expect_release == 42) {
- bcm47xx_wdt_stop();
- } else {
- pr_crit("Unexpected close, not stopping watchdog!\n");
- bcm47xx_wdt_start();
- }
-
- clear_bit(0, &bcm47xx_wdt_busy);
- expect_release = 0;
- return 0;
-}
-
-static ssize_t bcm47xx_wdt_write(struct file *file, const char __user *data,
- size_t len, loff_t *ppos)
-{
- if (len) {
- if (!nowayout) {
- size_t i;
-
- expect_release = 0;
-
- for (i = 0; i != len; i++) {
- char c;
- if (get_user(c, data + i))
- return -EFAULT;
- if (c == 'V')
- expect_release = 42;
- }
- }
- bcm47xx_wdt_pet();
- }
- return len;
-}
-
static const struct watchdog_info bcm47xx_wdt_info = {
.identity = DRV_NAME,
.options = WDIOF_SETTIMEOUT |
@@ -180,80 +130,25 @@ static const struct watchdog_info bcm47xx_wdt_info = {
WDIOF_MAGICCLOSE,
};
-static long bcm47xx_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, &bcm47xx_wdt_info,
- sizeof(bcm47xx_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) {
- bcm47xx_wdt_stop();
- retval = 0;
- }
-
- if (new_value & WDIOS_ENABLECARD) {
- bcm47xx_wdt_start();
- retval = 0;
- }
-
- return retval;
-
- case WDIOC_KEEPALIVE:
- bcm47xx_wdt_pet();
- return 0;
-
- case WDIOC_SETTIMEOUT:
- if (get_user(new_value, p))
- return -EFAULT;
-
- if (bcm47xx_wdt_settimeout(new_value))
- return -EINVAL;
-
- bcm47xx_wdt_pet();
-
- case WDIOC_GETTIMEOUT:
- return put_user(wdt_time, p);
-
- default:
- return -ENOTTY;
- }
-}
-
static int bcm47xx_wdt_notify_sys(struct notifier_block *this,
- unsigned long code, void *unused)
+ unsigned long code, void *unused)
{
if (code == SYS_DOWN || code == SYS_HALT)
bcm47xx_wdt_stop();
return NOTIFY_DONE;
}
-static const struct file_operations bcm47xx_wdt_fops = {
+static struct watchdog_ops bcm47xx_wdt_ops = {
.owner = THIS_MODULE,
- .llseek = no_llseek,
- .unlocked_ioctl = bcm47xx_wdt_ioctl,
- .open = bcm47xx_wdt_open,
- .release = bcm47xx_wdt_release,
- .write = bcm47xx_wdt_write,
+ .start = bcm47xx_wdt_start,
+ .stop = bcm47xx_wdt_stop,
+ .ping = bcm47xx_wdt_keepalive,
+ .set_timeout = bcm47xx_wdt_set_timeout,
};
-static struct miscdevice bcm47xx_wdt_miscdev = {
- .minor = WATCHDOG_MINOR,
- .name = "watchdog",
- .fops = &bcm47xx_wdt_fops,
+static struct watchdog_device bcm47xx_wdt_wdd = {
+ .info = &bcm47xx_wdt_info,
+ .ops = &bcm47xx_wdt_ops,
};
static struct notifier_block bcm47xx_wdt_notifier = {
@@ -274,12 +169,13 @@ static int __init bcm47xx_wdt_init(void)
pr_info("wdt_time value must be 0 < wdt_time < %d, using %d\n",
(WDT_MAX_TIME + 1), wdt_time);
}
+ watchdog_set_nowayout(&bcm47xx_wdt_wdd, nowayout);
ret = register_reboot_notifier(&bcm47xx_wdt_notifier);
if (ret)
return ret;
- ret = misc_register(&bcm47xx_wdt_miscdev);
+ ret = watchdog_register_device(&bcm47xx_wdt_wdd);
if (ret) {
unregister_reboot_notifier(&bcm47xx_wdt_notifier);
return ret;
@@ -292,10 +188,8 @@ static int __init bcm47xx_wdt_init(void)
static void __exit bcm47xx_wdt_exit(void)
{
- if (!nowayout)
- bcm47xx_wdt_stop();
-
- misc_deregister(&bcm47xx_wdt_miscdev);
+ watchdog_stop(&bcm47xx_wdt_wdd);
+ watchdog_unregister_device(&bcm47xx_wdt_wdd);
unregister_reboot_notifier(&bcm47xx_wdt_notifier);
}
@@ -306,4 +200,3 @@ module_exit(bcm47xx_wdt_exit);
MODULE_AUTHOR("Aleksandar Radovanovic");
MODULE_DESCRIPTION("Watchdog driver for Broadcom BCM47xx");
MODULE_LICENSE("GPL");
-MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
--
1.7.10.4
Some all ssb based devices have a PMU and the PMU watchdog should be
used and not the old one in chip common in this case. This patch also
calculates the maximal number the watchdog could be set to.
Signed-off-by: Hauke Mehrtens <[email protected]>
---
drivers/ssb/driver_chipcommon.c | 38 ++++++++++++++++++++++++++++++++++++--
1 file changed, 36 insertions(+), 2 deletions(-)
diff --git a/drivers/ssb/driver_chipcommon.c b/drivers/ssb/driver_chipcommon.c
index 7789f42..5631640 100644
--- a/drivers/ssb/driver_chipcommon.c
+++ b/drivers/ssb/driver_chipcommon.c
@@ -288,6 +288,24 @@ static u32 ssb_chipco_alp_clock(struct ssb_chipcommon *cc)
return 20000000;
}
+static u32 ssb_chipco_watchdog_get_max_timer(struct ssb_chipcommon *cc)
+{
+ u32 nb;
+
+ if (cc->capabilities & SSB_CHIPCO_CAP_PMU) {
+ if (cc->dev->id.revision < 26)
+ nb = 16;
+ else
+ nb = (cc->dev->id.revision >= 37) ? 32 : 24;
+ } else {
+ nb = 28;
+ }
+ if (nb == 32)
+ return 0xffffffff;
+ else
+ return (1 << nb) - 1;
+}
+
void ssb_chipcommon_init(struct ssb_chipcommon *cc)
{
if (!cc->dev)
@@ -405,8 +423,24 @@ void ssb_chipco_timing_init(struct ssb_chipcommon *cc,
/* Set chip watchdog reset timer to fire in 'ticks' backplane cycles */
void ssb_chipco_watchdog_timer_set(struct ssb_chipcommon *cc, u32 ticks)
{
- /* instant NMI */
- chipco_write32(cc, SSB_CHIPCO_WATCHDOG, ticks);
+ u32 maxt;
+ enum ssb_clkmode clkmode;
+
+ maxt = ssb_chipco_watchdog_get_max_timer(cc);
+ if (cc->capabilities & SSB_CHIPCO_CAP_PMU) {
+ if (ticks == 1)
+ ticks = 2;
+ else if (ticks > maxt)
+ ticks = maxt;
+ chipco_write32(cc, SSB_CHIPCO_PMU_WATCHDOG, ticks);
+ } else {
+ clkmode = ticks ? SSB_CLKMODE_FAST : SSB_CLKMODE_DYNAMIC;
+ ssb_chipco_set_clockmode(cc, clkmode);
+ if (ticks > maxt)
+ ticks = maxt;
+ /* instant NMI */
+ chipco_write32(cc, SSB_CHIPCO_WATCHDOG, ticks);
+ }
}
void ssb_chipco_irq_mask(struct ssb_chipcommon *cc, u32 mask, u32 value)
--
1.7.10.4
Rename the methods registered to struct watchdog_ops in order to add an
other struct watchdog_ops using different ops in the next patch.
Signed-off-by: Hauke Mehrtens <[email protected]>
---
drivers/watchdog/bcm47xx_wdt.c | 30 +++++++++++++++---------------
1 file changed, 15 insertions(+), 15 deletions(-)
diff --git a/drivers/watchdog/bcm47xx_wdt.c b/drivers/watchdog/bcm47xx_wdt.c
index cf1191b..66f2d2b 100644
--- a/drivers/watchdog/bcm47xx_wdt.c
+++ b/drivers/watchdog/bcm47xx_wdt.c
@@ -49,7 +49,7 @@ static inline struct bcm47xx_wdt *bcm47xx_wdt_get(struct watchdog_device *wdd)
return container_of(wdd, struct bcm47xx_wdt, wdd);
}
-static void bcm47xx_timer_tick(unsigned long data)
+static void bcm47xx_wdt_soft_timer_tick(unsigned long data)
{
struct bcm47xx_wdt *wdt = (struct bcm47xx_wdt *)data;
u32 next_tick = min(wdt->wdd.timeout * 1000, wdt->max_timer_ms);
@@ -62,7 +62,7 @@ static void bcm47xx_timer_tick(unsigned long data)
}
}
-static int bcm47xx_wdt_keepalive(struct watchdog_device *wdd)
+static int bcm47xx_wdt_soft_keepalive(struct watchdog_device *wdd)
{
struct bcm47xx_wdt *wdt = bcm47xx_wdt_get(wdd);
@@ -71,17 +71,17 @@ static int bcm47xx_wdt_keepalive(struct watchdog_device *wdd)
return 0;
}
-static int bcm47xx_wdt_start(struct watchdog_device *wdd)
+static int bcm47xx_wdt_soft_start(struct watchdog_device *wdd)
{
struct bcm47xx_wdt *wdt = bcm47xx_wdt_get(wdd);
- bcm47xx_wdt_keepalive(wdd);
- bcm47xx_timer_tick((unsigned long)wdt);
+ bcm47xx_wdt_soft_keepalive(wdd);
+ bcm47xx_wdt_soft_timer_tick((unsigned long)wdt);
return 0;
}
-static int bcm47xx_wdt_stop(struct watchdog_device *wdd)
+static int bcm47xx_wdt_soft_stop(struct watchdog_device *wdd)
{
struct bcm47xx_wdt *wdt = bcm47xx_wdt_get(wdd);
@@ -91,8 +91,8 @@ static int bcm47xx_wdt_stop(struct watchdog_device *wdd)
return 0;
}
-static int bcm47xx_wdt_set_timeout(struct watchdog_device *wdd,
- unsigned int new_time)
+static int bcm47xx_wdt_soft_set_timeout(struct watchdog_device *wdd,
+ unsigned int new_time)
{
if (new_time < 1 || new_time > WDT_MAX_TIME) {
pr_warn("timeout value must be 1<=x<=%d, using %d\n",
@@ -122,12 +122,12 @@ static int bcm47xx_wdt_notify_sys(struct notifier_block *this,
return NOTIFY_DONE;
}
-static struct watchdog_ops bcm47xx_wdt_ops = {
+static struct watchdog_ops bcm47xx_wdt_soft_ops = {
.owner = THIS_MODULE,
- .start = bcm47xx_wdt_start,
- .stop = bcm47xx_wdt_stop,
- .ping = bcm47xx_wdt_keepalive,
- .set_timeout = bcm47xx_wdt_set_timeout,
+ .start = bcm47xx_wdt_soft_start,
+ .stop = bcm47xx_wdt_soft_stop,
+ .ping = bcm47xx_wdt_soft_keepalive,
+ .set_timeout = bcm47xx_wdt_soft_set_timeout,
};
static int __devinit bcm47xx_wdt_probe(struct platform_device *pdev)
@@ -138,10 +138,10 @@ static int __devinit bcm47xx_wdt_probe(struct platform_device *pdev)
if (!wdt)
return -ENXIO;
- setup_timer(&wdt->soft_timer, bcm47xx_timer_tick,
+ setup_timer(&wdt->soft_timer, bcm47xx_wdt_soft_timer_tick,
(long unsigned int)wdt);
- wdt->wdd.ops = &bcm47xx_wdt_ops;
+ wdt->wdd.ops = &bcm47xx_wdt_soft_ops;
wdt->wdd.info = &bcm47xx_wdt_info;
wdt->wdd.timeout = WDT_DEFAULT_TIME;
ret = wdt->wdd.ops->set_timeout(&wdt->wdd, timeout);
--
1.7.10.4
The watchdog driver wants to set the watchdog timeout in ms and not in
ticks, which is depending on the SoC type and the clock.
Calculate the number of ticks per millisecond and provide two functions
for the watchdog driver. Also return the ticks or millisecond the timer
was set to in case the provided value was bigger than the max allowed
value.
Signed-off-by: Hauke Mehrtens <[email protected]>
---
drivers/ssb/driver_chipcommon.c | 47 ++++++++++++++++++++++++++++-
drivers/ssb/ssb_private.h | 5 +++
include/linux/ssb/ssb_driver_chipcommon.h | 5 +--
3 files changed, 54 insertions(+), 3 deletions(-)
diff --git a/drivers/ssb/driver_chipcommon.c b/drivers/ssb/driver_chipcommon.c
index 5631640..92de046 100644
--- a/drivers/ssb/driver_chipcommon.c
+++ b/drivers/ssb/driver_chipcommon.c
@@ -4,6 +4,7 @@
*
* Copyright 2005, Broadcom Corporation
* Copyright 2006, 2007, Michael Buesch <[email protected]>
+ * Copyright 2012, Hauke Mehrtens <[email protected]>
*
* Licensed under the GNU/GPL. See COPYING for details.
*/
@@ -12,6 +13,7 @@
#include <linux/ssb/ssb_regs.h>
#include <linux/export.h>
#include <linux/pci.h>
+#include <linux/bcm47xx_wdt.h>
#include "ssb_private.h"
@@ -306,6 +308,43 @@ static u32 ssb_chipco_watchdog_get_max_timer(struct ssb_chipcommon *cc)
return (1 << nb) - 1;
}
+u32 ssb_chipco_watchdog_timer_set_wdt(struct bcm47xx_wdt *wdt, u32 ticks)
+{
+ struct ssb_chipcommon *cc = bcm47xx_wdt_get_drvdata(wdt);
+
+ if (cc->dev->bus->bustype != SSB_BUSTYPE_SSB)
+ return 0;
+
+ return ssb_chipco_watchdog_timer_set(cc, ticks);
+}
+
+u32 ssb_chipco_watchdog_timer_set_ms(struct bcm47xx_wdt *wdt, u32 ms)
+{
+ struct ssb_chipcommon *cc = bcm47xx_wdt_get_drvdata(wdt);
+ u32 ticks;
+
+ if (cc->dev->bus->bustype != SSB_BUSTYPE_SSB)
+ return 0;
+
+ ticks = ssb_chipco_watchdog_timer_set(cc, cc->ticks_per_ms * ms);
+ return ticks / cc->ticks_per_ms;
+}
+
+static int ssb_chipco_watchdog_ticks_per_ms(struct ssb_chipcommon *cc)
+{
+ struct ssb_bus *bus = cc->dev->bus;
+
+ if (cc->capabilities & SSB_CHIPCO_CAP_PMU) {
+ /* based on 32KHz ILP clock */
+ return 32;
+ } else {
+ if (cc->dev->id.revision < 18)
+ return ssb_clockspeed(bus) / 1000;
+ else
+ return ssb_chipco_alp_clock(cc) / 1000;
+ }
+}
+
void ssb_chipcommon_init(struct ssb_chipcommon *cc)
{
if (!cc->dev)
@@ -323,6 +362,11 @@ void ssb_chipcommon_init(struct ssb_chipcommon *cc)
chipco_powercontrol_init(cc);
ssb_chipco_set_clockmode(cc, SSB_CLKMODE_FAST);
calc_fast_powerup_delay(cc);
+
+ if (cc->dev->bus->bustype == SSB_BUSTYPE_SSB) {
+ cc->ticks_per_ms = ssb_chipco_watchdog_ticks_per_ms(cc);
+ cc->max_timer_ms = ssb_chipco_watchdog_get_max_timer(cc) / cc->ticks_per_ms;
+ }
}
void ssb_chipco_suspend(struct ssb_chipcommon *cc)
@@ -421,7 +465,7 @@ void ssb_chipco_timing_init(struct ssb_chipcommon *cc,
}
/* Set chip watchdog reset timer to fire in 'ticks' backplane cycles */
-void ssb_chipco_watchdog_timer_set(struct ssb_chipcommon *cc, u32 ticks)
+u32 ssb_chipco_watchdog_timer_set(struct ssb_chipcommon *cc, u32 ticks)
{
u32 maxt;
enum ssb_clkmode clkmode;
@@ -441,6 +485,7 @@ void ssb_chipco_watchdog_timer_set(struct ssb_chipcommon *cc, u32 ticks)
/* instant NMI */
chipco_write32(cc, SSB_CHIPCO_WATCHDOG, ticks);
}
+ return ticks;
}
void ssb_chipco_irq_mask(struct ssb_chipcommon *cc, u32 mask, u32 value)
diff --git a/drivers/ssb/ssb_private.h b/drivers/ssb/ssb_private.h
index 98b2915..03cc40a 100644
--- a/drivers/ssb/ssb_private.h
+++ b/drivers/ssb/ssb_private.h
@@ -3,6 +3,7 @@
#include <linux/ssb/ssb.h>
#include <linux/types.h>
+#include <linux/bcm47xx_wdt.h>
#define PFX "ssb: "
@@ -212,4 +213,8 @@ extern u32 ssb_pmu_get_cpu_clock(struct ssb_chipcommon *cc);
extern u32 ssb_pmu_get_controlclock(struct ssb_chipcommon *cc);
extern u32 ssb_pmu_get_alp_clock(struct ssb_chipcommon *cc);
+extern u32 ssb_chipco_watchdog_timer_set_wdt(struct bcm47xx_wdt *wdt,
+ u32 ticks);
+extern u32 ssb_chipco_watchdog_timer_set_ms(struct bcm47xx_wdt *wdt, u32 ms);
+
#endif /* LINUX_SSB_PRIVATE_H_ */
diff --git a/include/linux/ssb/ssb_driver_chipcommon.h b/include/linux/ssb/ssb_driver_chipcommon.h
index c2b02a5..38339fd 100644
--- a/include/linux/ssb/ssb_driver_chipcommon.h
+++ b/include/linux/ssb/ssb_driver_chipcommon.h
@@ -591,6 +591,8 @@ struct ssb_chipcommon {
/* Fast Powerup Delay constant */
u16 fast_pwrup_delay;
struct ssb_chipcommon_pmu pmu;
+ u32 ticks_per_ms;
+ u32 max_timer_ms;
};
static inline bool ssb_chipco_available(struct ssb_chipcommon *cc)
@@ -630,8 +632,7 @@ enum ssb_clkmode {
extern void ssb_chipco_set_clockmode(struct ssb_chipcommon *cc,
enum ssb_clkmode mode);
-extern void ssb_chipco_watchdog_timer_set(struct ssb_chipcommon *cc,
- u32 ticks);
+extern u32 ssb_chipco_watchdog_timer_set(struct ssb_chipcommon *cc, u32 ticks);
void ssb_chipco_irq_mask(struct ssb_chipcommon *cc, u32 mask, u32 value);
--
1.7.10.4
When there is a PMU in the device get the alp clock from that part and
do not assume 20000000.
Signed-off-by: Hauke Mehrtens <[email protected]>
---
drivers/ssb/driver_chipcommon.c | 14 +++++++++-----
drivers/ssb/driver_chipcommon_pmu.c | 27 +++++++++++++++++++++++++++
drivers/ssb/ssb_private.h | 1 +
3 files changed, 37 insertions(+), 5 deletions(-)
diff --git a/drivers/ssb/driver_chipcommon.c b/drivers/ssb/driver_chipcommon.c
index e9d2ca1..7789f42 100644
--- a/drivers/ssb/driver_chipcommon.c
+++ b/drivers/ssb/driver_chipcommon.c
@@ -280,6 +280,14 @@ static void calc_fast_powerup_delay(struct ssb_chipcommon *cc)
cc->fast_pwrup_delay = tmp;
}
+static u32 ssb_chipco_alp_clock(struct ssb_chipcommon *cc)
+{
+ if (cc->capabilities & SSB_CHIPCO_CAP_PMU)
+ return ssb_pmu_get_alp_clock(cc);
+
+ return 20000000;
+}
+
void ssb_chipcommon_init(struct ssb_chipcommon *cc)
{
if (!cc->dev)
@@ -474,11 +482,7 @@ int ssb_chipco_serial_init(struct ssb_chipcommon *cc,
| SSB_CHIPCO_CORECTL_UARTCLK0);
} else if ((ccrev >= 11) && (ccrev != 15)) {
/* Fixed ALP clock */
- baud_base = 20000000;
- if (cc->capabilities & SSB_CHIPCO_CAP_PMU) {
- /* FIXME: baud_base is different for devices with a PMU */
- SSB_WARN_ON(1);
- }
+ baud_base = ssb_chipco_alp_clock(cc);
div = 1;
if (ccrev >= 21) {
/* Turn off UART clock before switching clocksource. */
diff --git a/drivers/ssb/driver_chipcommon_pmu.c b/drivers/ssb/driver_chipcommon_pmu.c
index d7d5804..a43415a 100644
--- a/drivers/ssb/driver_chipcommon_pmu.c
+++ b/drivers/ssb/driver_chipcommon_pmu.c
@@ -618,6 +618,33 @@ void ssb_pmu_set_ldo_paref(struct ssb_chipcommon *cc, bool on)
EXPORT_SYMBOL(ssb_pmu_set_ldo_voltage);
EXPORT_SYMBOL(ssb_pmu_set_ldo_paref);
+static u32 ssb_pmu_get_alp_clock_clk0(struct ssb_chipcommon *cc)
+{
+ u32 crystalfreq;
+ const struct pmu0_plltab_entry *e = NULL;
+
+ crystalfreq = chipco_read32(cc, SSB_CHIPCO_PMU_CTL) &
+ SSB_CHIPCO_PMU_CTL_XTALFREQ >> SSB_CHIPCO_PMU_CTL_XTALFREQ_SHIFT;
+ e = pmu0_plltab_find_entry(crystalfreq);
+ BUG_ON(!e);
+ return e->freq * 1000;
+}
+
+u32 ssb_pmu_get_alp_clock(struct ssb_chipcommon *cc)
+{
+ struct ssb_bus *bus = cc->dev->bus;
+
+ switch (bus->chip_id) {
+ case 0x5354:
+ ssb_pmu_get_alp_clock_clk0(cc);
+ default:
+ ssb_printk(KERN_ERR PFX
+ "ERROR: PMU alp clock unknown for device %04X\n",
+ bus->chip_id);
+ return 0;
+ }
+}
+
u32 ssb_pmu_get_cpu_clock(struct ssb_chipcommon *cc)
{
struct ssb_bus *bus = cc->dev->bus;
diff --git a/drivers/ssb/ssb_private.h b/drivers/ssb/ssb_private.h
index a305550..98b2915 100644
--- a/drivers/ssb/ssb_private.h
+++ b/drivers/ssb/ssb_private.h
@@ -210,5 +210,6 @@ static inline void b43_pci_ssb_bridge_exit(void)
/* driver_chipcommon_pmu.c */
extern u32 ssb_pmu_get_cpu_clock(struct ssb_chipcommon *cc);
extern u32 ssb_pmu_get_controlclock(struct ssb_chipcommon *cc);
+extern u32 ssb_pmu_get_alp_clock(struct ssb_chipcommon *cc);
#endif /* LINUX_SSB_PRIVATE_H_ */
--
1.7.10.4
For devices without a PMU the alp clock is always 20000000.
Signed-off-by: Hauke Mehrtens <[email protected]>
---
drivers/bcma/driver_chipcommon.c | 12 ++++++++++--
1 file changed, 10 insertions(+), 2 deletions(-)
diff --git a/drivers/bcma/driver_chipcommon.c b/drivers/bcma/driver_chipcommon.c
index ffd74e5..ef68553 100644
--- a/drivers/bcma/driver_chipcommon.c
+++ b/drivers/bcma/driver_chipcommon.c
@@ -4,6 +4,7 @@
*
* Copyright 2005, Broadcom Corporation
* Copyright 2006, 2007, Michael Buesch <[email protected]>
+ * Copyright 2012, Hauke Mehrtens <[email protected]>
*
* Licensed under the GNU/GPL. See COPYING for details.
*/
@@ -22,6 +23,14 @@ static inline u32 bcma_cc_write32_masked(struct bcma_drv_cc *cc, u16 offset,
return value;
}
+static u32 bcma_chipco_alp_clock(struct bcma_drv_cc *cc)
+{
+ if (cc->capabilities & BCMA_CC_CAP_PMU)
+ return bcma_pmu_alp_clock(cc);
+
+ return 20000000;
+}
+
void bcma_core_chipcommon_early_init(struct bcma_drv_cc *cc)
{
if (cc->early_setup_done)
@@ -131,8 +140,7 @@ void bcma_chipco_serial_init(struct bcma_drv_cc *cc)
struct bcma_serial_port *ports = cc->serial_ports;
if (ccrev >= 11 && ccrev != 15) {
- /* Fixed ALP clock */
- baud_base = bcma_pmu_alp_clock(cc);
+ baud_base = bcma_chipco_alp_clock(cc);
if (ccrev >= 21) {
/* Turn off UART clock before switching clocksource. */
bcma_cc_write32(cc, BCMA_CC_CORECTL,
--
1.7.10.4
Prevent the watchdog register ion the extif core to be set to a too
high value.
Signed-off-by: Hauke Mehrtens <[email protected]>
---
drivers/ssb/driver_extif.c | 5 +++--
include/linux/ssb/ssb_driver_extif.h | 1 +
2 files changed, 4 insertions(+), 2 deletions(-)
diff --git a/drivers/ssb/driver_extif.c b/drivers/ssb/driver_extif.c
index dc47f30..0aa4c2a 100644
--- a/drivers/ssb/driver_extif.c
+++ b/drivers/ssb/driver_extif.c
@@ -112,9 +112,10 @@ void ssb_extif_get_clockcontrol(struct ssb_extif *extif,
*m = extif_read32(extif, SSB_EXTIF_CLOCK_SB);
}
-void ssb_extif_watchdog_timer_set(struct ssb_extif *extif,
- u32 ticks)
+void ssb_extif_watchdog_timer_set(struct ssb_extif *extif, u32 ticks)
{
+ if (ticks > SSB_EXTIF_WATCHDOG_MAX_TIMER)
+ ticks = SSB_EXTIF_WATCHDOG_MAX_TIMER;
extif_write32(extif, SSB_EXTIF_WATCHDOG, ticks);
}
diff --git a/include/linux/ssb/ssb_driver_extif.h b/include/linux/ssb/ssb_driver_extif.h
index 91161f0..34072f5 100644
--- a/include/linux/ssb/ssb_driver_extif.h
+++ b/include/linux/ssb/ssb_driver_extif.h
@@ -152,6 +152,7 @@
/* watchdog */
#define SSB_EXTIF_WATCHDOG_CLK 48000000 /* Hz */
+#define SSB_EXTIF_WATCHDOG_MAX_TIMER ((1 << 28) - 1)
#ifdef CONFIG_SSB_DRIVER_EXTIF
--
1.7.10.4
On Sat, Nov 24, 2012 at 11:24:00PM +0100, Hauke Mehrtens wrote:
> This patch series improves the watchdog driver used on the Broadcom
> bcm47xx SoCs.
> The watchdog driver does not access the functions directly any more,
> but it registers as a platform device driver and ssb and bcma are
> registering a device for this watchdog driver.
> This also adds support for SoCs with a power management unit (PMU),
> which have different clock rates.
>
> This code is currently based on the wireless-testing/master tree by
> John Linville, because there are some changes in ssb and bcma in that
> tree queued for 3.8 which will conflict with these changes, if this
> would be based on an other tree. I have no problem with rebasing this
> onto any other tree.
I'm happy to take this series through the wireless-next tree, if
someone will ACK the bcm47xx bits...
John
> Hauke Mehrtens (15):
> watchdog: bcm47xx_wdt.c: convert to watchdog core api
> watchdog: bcm47xx_wdt.c: use platform device
> watchdog: bcm47xx_wdt.c: rename ops methods
> watchdog: bcm47xx_wdt.c: rename wdt_timeout to timeout
> watchdog: bcm47xx_wdt.c: add hard timer
> bcma: add bcma_chipco_alp_clock
> bcma: set the pmu watchdog if available
> bcma: add methods for watchdog driver
> bcma: register watchdog driver
> ssb: get alp clock from devices with PMU
> ssb: set the pmu watchdog if available
> ssb: add methods for watchdog driver
> ssb: extif: add check for max value in watchdog
> ssb: extif: add methods for watchdog driver
> ssb: register watchdog driver
>
> drivers/bcma/bcma_private.h | 2 +
> drivers/bcma/driver_chipcommon.c | 114 ++++++++-
> drivers/bcma/main.c | 8 +
> drivers/ssb/driver_chipcommon.c | 99 +++++++-
> drivers/ssb/driver_chipcommon_pmu.c | 27 +++
> drivers/ssb/driver_extif.c | 24 +-
> drivers/ssb/embedded.c | 35 +++
> drivers/ssb/main.c | 8 +
> drivers/ssb/ssb_private.h | 31 +++
> drivers/watchdog/Kconfig | 1 +
> drivers/watchdog/bcm47xx_wdt.c | 339 ++++++++++++---------------
> include/linux/bcm47xx_wdt.h | 28 +++
> include/linux/bcma/bcma_driver_chipcommon.h | 7 +-
> include/linux/ssb/ssb.h | 2 +
> include/linux/ssb/ssb_driver_chipcommon.h | 5 +-
> include/linux/ssb/ssb_driver_extif.h | 10 +-
> 16 files changed, 522 insertions(+), 218 deletions(-)
> create mode 100644 include/linux/bcm47xx_wdt.h
>
> --
> 1.7.10.4
>
>
--
John W. Linville Someday the world will need a hero, and you
[email protected] might be all we have. Be ready.
The more recent devices have a watchdog timer which could be configured
for over 2 hours and not just 2 seconds like the first generation
devices. For those devices do not use the extra software timer, but
directly program the time into the register. This will automatically be
used if the timer supports more than a minute.
Signed-off-by: Hauke Mehrtens <[email protected]>
---
drivers/watchdog/bcm47xx_wdt.c | 69 ++++++++++++++++++++++++++++++++++++----
include/linux/bcm47xx_wdt.h | 1 +
2 files changed, 64 insertions(+), 6 deletions(-)
diff --git a/drivers/watchdog/bcm47xx_wdt.c b/drivers/watchdog/bcm47xx_wdt.c
index b6a8c49..c77f5ca 100644
--- a/drivers/watchdog/bcm47xx_wdt.c
+++ b/drivers/watchdog/bcm47xx_wdt.c
@@ -31,6 +31,7 @@
#define WDT_DEFAULT_TIME 30 /* seconds */
#define WDT_SOFTTIMER_MAX 3600 /* seconds */
+#define WDT_SOFTTIMER_THRESHOLD 60 /* seconds */
static int timeout = WDT_DEFAULT_TIME;
static bool nowayout = WATCHDOG_NOWAYOUT;
@@ -49,6 +50,53 @@ static inline struct bcm47xx_wdt *bcm47xx_wdt_get(struct watchdog_device *wdd)
return container_of(wdd, struct bcm47xx_wdt, wdd);
}
+static int bcm47xx_wdt_hard_keepalive(struct watchdog_device *wdd)
+{
+ struct bcm47xx_wdt *wdt = bcm47xx_wdt_get(wdd);
+
+ wdt->timer_set_ms(wdt, wdd->timeout * 1000);
+
+ return 0;
+}
+
+static int bcm47xx_wdt_hard_start(struct watchdog_device *wdd)
+{
+ return 0;
+}
+
+static int bcm47xx_wdt_hard_stop(struct watchdog_device *wdd)
+{
+ struct bcm47xx_wdt *wdt = bcm47xx_wdt_get(wdd);
+
+ wdt->timer_set(wdt, 0);
+
+ return 0;
+}
+
+static int bcm47xx_wdt_hard_set_timeout(struct watchdog_device *wdd,
+ unsigned int new_time)
+{
+ struct bcm47xx_wdt *wdt = bcm47xx_wdt_get(wdd);
+ u32 max_timer = wdt->max_timer_ms;
+
+ if (new_time < 1 || new_time > max_timer / 1000) {
+ pr_warn("timeout value must be 1<=x<=%d, using %d\n",
+ max_timer / 1000, new_time);
+ return -EINVAL;
+ }
+
+ wdd->timeout = new_time;
+ return 0;
+}
+
+static struct watchdog_ops bcm47xx_wdt_hard_ops = {
+ .owner = THIS_MODULE,
+ .start = bcm47xx_wdt_hard_start,
+ .stop = bcm47xx_wdt_hard_stop,
+ .ping = bcm47xx_wdt_hard_keepalive,
+ .set_timeout = bcm47xx_wdt_hard_set_timeout,
+};
+
static void bcm47xx_wdt_soft_timer_tick(unsigned long data)
{
struct bcm47xx_wdt *wdt = (struct bcm47xx_wdt *)data;
@@ -133,15 +181,22 @@ static struct watchdog_ops bcm47xx_wdt_soft_ops = {
static int __devinit bcm47xx_wdt_probe(struct platform_device *pdev)
{
int ret;
+ bool soft;
struct bcm47xx_wdt *wdt = dev_get_platdata(&pdev->dev);
if (!wdt)
return -ENXIO;
- setup_timer(&wdt->soft_timer, bcm47xx_wdt_soft_timer_tick,
- (long unsigned int)wdt);
+ soft = wdt->max_timer_ms < WDT_SOFTTIMER_THRESHOLD * 1000;
+
+ if (soft) {
+ wdt->wdd.ops = &bcm47xx_wdt_soft_ops;
+ setup_timer(&wdt->soft_timer, bcm47xx_wdt_soft_timer_tick,
+ (long unsigned int)wdt);
+ } else {
+ wdt->wdd.ops = &bcm47xx_wdt_hard_ops;
+ }
- wdt->wdd.ops = &bcm47xx_wdt_soft_ops;
wdt->wdd.info = &bcm47xx_wdt_info;
wdt->wdd.timeout = WDT_DEFAULT_TIME;
ret = wdt->wdd.ops->set_timeout(&wdt->wdd, timeout);
@@ -159,14 +214,16 @@ static int __devinit bcm47xx_wdt_probe(struct platform_device *pdev)
if (ret)
goto err_notifier;
- pr_info("BCM47xx Watchdog Timer enabled (%d seconds%s)\n",
- timeout, nowayout ? ", nowayout" : "");
+ dev_info(&pdev->dev, "BCM47xx Watchdog Timer enabled (%d seconds%s%s)\n",
+ timeout, nowayout ? ", nowayout" : "",
+ soft ? ", Software Timer" : "");
return 0;
err_notifier:
unregister_reboot_notifier(&wdt->notifier);
err_timer:
- del_timer_sync(&wdt->soft_timer);
+ if (soft)
+ del_timer_sync(&wdt->soft_timer);
return ret;
}
diff --git a/include/linux/bcm47xx_wdt.h b/include/linux/bcm47xx_wdt.h
index b94ac0e..b708786 100644
--- a/include/linux/bcm47xx_wdt.h
+++ b/include/linux/bcm47xx_wdt.h
@@ -10,6 +10,7 @@
struct bcm47xx_wdt {
u32 (*timer_set)(struct bcm47xx_wdt *, u32);
u32 (*timer_set_ms)(struct bcm47xx_wdt *, u32);
+ u32 max_timer_ms;
void *driver_data;
--
1.7.10.4