summaryrefslogtreecommitdiffstats
path: root/target
diff options
context:
space:
mode:
authorhauke <hauke@3c298f89-4303-0410-b956-a3cf2f4a3e73>2012-11-24 20:07:25 +0000
committerhauke <hauke@3c298f89-4303-0410-b956-a3cf2f4a3e73>2012-11-24 20:07:25 +0000
commit9a462204d13bd8acd735f8111dc7f9412e812b33 (patch)
tree90fc287e9ed841c2ec2972694366a16375b511fd /target
parent0b36fa881c11ebe7deae587bd6cae851d70f7477 (diff)
brcm47xx: update watchdog driver
This watchdog driver should work with SoC having a PMU. This fixes #11720. git-svn-id: svn://svn.openwrt.org/openwrt/trunk@34323 3c298f89-4303-0410-b956-a3cf2f4a3e73
Diffstat (limited to 'target')
-rw-r--r--target/linux/brcm47xx/config-3.61
-rw-r--r--target/linux/brcm47xx/patches-3.6/540-watchdog-bcm47xx_wdt.c-convert-to-watchdog-core-api.patch252
-rw-r--r--target/linux/brcm47xx/patches-3.6/541-watchdog-bcm47xx_wdt.c-use-platform-device.patch288
-rw-r--r--target/linux/brcm47xx/patches-3.6/542-watchdog-bcm47xx_wdt.c-rename-ops-methods.patch90
-rw-r--r--target/linux/brcm47xx/patches-3.6/543-watchdog-bcm47xx_wdt.c-rename-wdt_timeout-to-timeout.patch41
-rw-r--r--target/linux/brcm47xx/patches-3.6/544-watchdog-bcm47xx_wdt.c-add-hard-timer.patch120
-rw-r--r--target/linux/brcm47xx/patches-3.6/545-bcma-add-bcma_chipco_alp_clock.patch35
-rw-r--r--target/linux/brcm47xx/patches-3.6/546-bcma-set-the-pmu-watchdog-if-available.patch57
-rw-r--r--target/linux/brcm47xx/patches-3.6/547-bcma-add-methods-for-watchdog-driver.patch93
-rw-r--r--target/linux/brcm47xx/patches-3.6/548-bcma-register-watchdog-driver.patch92
-rw-r--r--target/linux/brcm47xx/patches-3.6/549-ssb-get-alp-clock-from-devices-with-PMU.patch76
-rw-r--r--target/linux/brcm47xx/patches-3.6/550-ssb-set-the-pmu-watchdog-if-available.patch54
-rw-r--r--target/linux/brcm47xx/patches-3.6/551-ssb-add-methods-for-watchdog-driver.patch131
-rw-r--r--target/linux/brcm47xx/patches-3.6/552-ssb-extif-add-check-for-max-value-in-watchdog.patch25
-rw-r--r--target/linux/brcm47xx/patches-3.6/553-ssb-extif-add-methods-for-watchdog-driver.patch89
-rw-r--r--target/linux/brcm47xx/patches-3.6/554-ssb-register-watchdog-driver.patch122
-rw-r--r--target/linux/brcm47xx/patches-3.6/900-bcm47xx_wdt-noprescale.patch103
17 files changed, 1566 insertions, 103 deletions
diff --git a/target/linux/brcm47xx/config-3.6 b/target/linux/brcm47xx/config-3.6
index 278e58cf0..4859a5b6d 100644
--- a/target/linux/brcm47xx/config-3.6
+++ b/target/linux/brcm47xx/config-3.6
@@ -144,5 +144,6 @@ CONFIG_USB_ARCH_HAS_XHCI=y
# CONFIG_USB_HCD_BCMA is not set
# CONFIG_USB_HCD_SSB is not set
CONFIG_USB_SUPPORT=y
+CONFIG_WATCHDOG_CORE=y
CONFIG_WATCHDOG_NOWAYOUT=y
CONFIG_ZONE_DMA_FLAG=0
diff --git a/target/linux/brcm47xx/patches-3.6/540-watchdog-bcm47xx_wdt.c-convert-to-watchdog-core-api.patch b/target/linux/brcm47xx/patches-3.6/540-watchdog-bcm47xx_wdt.c-convert-to-watchdog-core-api.patch
new file mode 100644
index 000000000..7ac56387f
--- /dev/null
+++ b/target/linux/brcm47xx/patches-3.6/540-watchdog-bcm47xx_wdt.c-convert-to-watchdog-core-api.patch
@@ -0,0 +1,252 @@
+--- 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.
+
+--- 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>
+@@ -48,8 +45,6 @@ MODULE_PARM_DESC(nowayout,
+ __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 +92,31 @@ static void bcm47xx_timer_tick(unsigned
+ }
+ }
+
+-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 +125,6 @@ static int bcm47xx_wdt_settimeout(int ne
+ 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 +132,25 @@ static const struct watchdog_info bcm47x
+ 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 +171,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 +190,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 +202,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);
diff --git a/target/linux/brcm47xx/patches-3.6/541-watchdog-bcm47xx_wdt.c-use-platform-device.patch b/target/linux/brcm47xx/patches-3.6/541-watchdog-bcm47xx_wdt.c-use-platform-device.patch
new file mode 100644
index 000000000..79bb962f7
--- /dev/null
+++ b/target/linux/brcm47xx/patches-3.6/541-watchdog-bcm47xx_wdt.c-use-platform-device.patch
@@ -0,0 +1,288 @@
+--- a/drivers/watchdog/bcm47xx_wdt.c
++++ b/drivers/watchdog/bcm47xx_wdt.c
+@@ -3,6 +3,7 @@
+ *
+ * Copyright (C) 2008 Aleksandar Radovanovic <biblbroks@sezampro.rs>
+ * Copyright (C) 2009 Matthieu CASTET <castet.matthieu@free.fr>
++ * Copyright (C) 2012 Hauke Mehrtens <hauke@hauke-m.de>
+ *
+ * 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"
+
+@@ -45,48 +46,19 @@ MODULE_PARM_DESC(nowayout,
+ __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
+ #endif
+
+-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");
+ }
+@@ -94,23 +66,29 @@ static void bcm47xx_timer_tick(unsigned
+
+ 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;
+ }
+@@ -118,10 +96,13 @@ static int bcm47xx_wdt_stop(struct watch
+ 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;
+ }
+
+@@ -135,8 +116,11 @@ static const struct watchdog_info bcm47x
+ 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;
+ }
+
+@@ -148,57 +132,72 @@ static struct watchdog_ops bcm47xx_wdt_o
+ .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 <hauke@hauke-m.de>");
+ MODULE_DESCRIPTION("Watchdog driver for Broadcom BCM47xx");
+ MODULE_LICENSE("GPL");
+--- /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_ */
diff --git a/target/linux/brcm47xx/patches-3.6/542-watchdog-bcm47xx_wdt.c-rename-ops-methods.patch b/target/linux/brcm47xx/patches-3.6/542-watchdog-bcm47xx_wdt.c-rename-ops-methods.patch
new file mode 100644
index 000000000..54956fd2d
--- /dev/null
+++ b/target/linux/brcm47xx/patches-3.6/542-watchdog-bcm47xx_wdt.c-rename-ops-methods.patch
@@ -0,0 +1,90 @@
+--- a/drivers/watchdog/bcm47xx_wdt.c
++++ b/drivers/watchdog/bcm47xx_wdt.c
+@@ -46,12 +46,13 @@ MODULE_PARM_DESC(nowayout,
+ __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
+ #endif
+
++
+ 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);
+@@ -64,7 +65,7 @@ static void bcm47xx_timer_tick(unsigned
+ }
+ }
+
+-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);
+
+@@ -73,17 +74,17 @@ static int bcm47xx_wdt_keepalive(struct
+ 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);
+
+@@ -93,8 +94,8 @@ static int bcm47xx_wdt_stop(struct watch
+ 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",
+@@ -124,12 +125,12 @@ static int bcm47xx_wdt_notify_sys(struct
+ 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)
+@@ -140,10 +141,10 @@ static int __devinit bcm47xx_wdt_probe(s
+ 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);
diff --git a/target/linux/brcm47xx/patches-3.6/543-watchdog-bcm47xx_wdt.c-rename-wdt_timeout-to-timeout.patch b/target/linux/brcm47xx/patches-3.6/543-watchdog-bcm47xx_wdt.c-rename-wdt_timeout-to-timeout.patch
new file mode 100644
index 000000000..c08f0b153
--- /dev/null
+++ b/target/linux/brcm47xx/patches-3.6/543-watchdog-bcm47xx_wdt.c-rename-wdt_timeout-to-timeout.patch
@@ -0,0 +1,41 @@
+--- 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) ")");
+
+ #ifdef CONFIG_WATCHDOG_NOWAYOUT
+@@ -97,9 +97,9 @@ static int bcm47xx_wdt_soft_stop(struct
+ 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;
+ }
+
+@@ -163,7 +163,7 @@ static int __devinit bcm47xx_wdt_probe(s
+ goto err_notifier;
+
+ pr_info("BCM47xx Watchdog Timer enabled (%d seconds%s)\n",
+- wdt_time, nowayout ? ", nowayout" : "");
++ timeout, nowayout ? ", nowayout" : "");
+ return 0;
+
+ err_notifier:
diff --git a/target/linux/brcm47xx/patches-3.6/544-watchdog-bcm47xx_wdt.c-add-hard-timer.patch b/target/linux/brcm47xx/patches-3.6/544-watchdog-bcm47xx_wdt.c-add-hard-timer.patch
new file mode 100644
index 000000000..3e67ba2bb
--- /dev/null
+++ b/target/linux/brcm47xx/patches-3.6/544-watchdog-bcm47xx_wdt.c-add-hard-timer.patch
@@ -0,0 +1,120 @@
+--- 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;
+@@ -52,6 +53,53 @@ static inline struct bcm47xx_wdt *bcm47x
+ 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;
+@@ -136,15 +184,22 @@ static struct watchdog_ops bcm47xx_wdt_s
+ 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);
+@@ -162,14 +217,16 @@ static int __devinit bcm47xx_wdt_probe(s
+ 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;
+ }
+--- 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;
+
diff --git a/target/linux/brcm47xx/patches-3.6/545-bcma-add-bcma_chipco_alp_clock.patch b/target/linux/brcm47xx/patches-3.6/545-bcma-add-bcma_chipco_alp_clock.patch
new file mode 100644
index 000000000..f6f0012a7
--- /dev/null
+++ b/target/linux/brcm47xx/patches-3.6/545-bcma-add-bcma_chipco_alp_clock.patch
@@ -0,0 +1,35 @@
+--- 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 <m@bues.ch>
++ * Copyright 2012, Hauke Mehrtens <hauke@hauke-m.de>
+ *
+ * Licensed under the GNU/GPL. See COPYING for details.
+ */
+@@ -22,6 +23,14 @@ static inline u32 bcma_cc_write32_masked
+ 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)
+@@ -180,8 +189,7 @@ void bcma_chipco_serial_init(struct bcma
+ 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,
diff --git a/target/linux/brcm47xx/patches-3.6/546-bcma-set-the-pmu-watchdog-if-available.patch b/target/linux/brcm47xx/patches-3.6/546-bcma-set-the-pmu-watchdog-if-available.patch
new file mode 100644
index 000000000..651415eb9
--- /dev/null
+++ b/target/linux/brcm47xx/patches-3.6/546-bcma-set-the-pmu-watchdog-if-available.patch
@@ -0,0 +1,57 @@
+--- a/drivers/bcma/driver_chipcommon.c
++++ b/drivers/bcma/driver_chipcommon.c
+@@ -31,6 +31,28 @@ static u32 bcma_chipco_alp_clock(struct
+ 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)
+@@ -87,8 +109,23 @@ void bcma_core_chipcommon_init(struct bc
+ /* 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;
++ bcma_cc_write32(cc, BCMA_CC_WATCHDOG, ticks);
++ }
+ }
+
+ void bcma_chipco_irq_mask(struct bcma_drv_cc *cc, u32 mask, u32 value)
diff --git a/target/linux/brcm47xx/patches-3.6/547-bcma-add-methods-for-watchdog-driver.patch b/target/linux/brcm47xx/patches-3.6/547-bcma-add-methods-for-watchdog-driver.patch
new file mode 100644
index 000000000..1be2476d3
--- /dev/null
+++ b/target/linux/brcm47xx/patches-3.6/547-bcma-add-methods-for-watchdog-driver.patch
@@ -0,0 +1,93 @@
+--- 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_
+ 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)
+ {
+@@ -102,12 +136,13 @@ void bcma_core_chipcommon_init(struct bc
+ }
+
+ spin_lock_init(&cc->gpio_lock);
++ 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;
+@@ -126,6 +161,7 @@ void bcma_chipco_watchdog_timer_set(stru
+ ticks = maxt;
+ bcma_cc_write32(cc, BCMA_CC_WATCHDOG, ticks);
+ }
++ return ticks;
+ }
+
+ void bcma_chipco_irq_mask(struct bcma_drv_cc *cc, u32 mask, u32 value)
+--- a/include/linux/bcma/bcma_driver_chipcommon.h
++++ b/include/linux/bcma/bcma_driver_chipcommon.h
+@@ -554,6 +554,7 @@ struct bcma_drv_cc {
+
+ /* Lock for GPIO register access. */
+ spinlock_t gpio_lock;
++ u32 ticks_per_ms;
+ };
+
+ /* Register access */
+@@ -577,8 +578,7 @@ extern void bcma_chipco_resume(struct bc
+
+ 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);
+
diff --git a/target/linux/brcm47xx/patches-3.6/548-bcma-register-watchdog-driver.patch b/target/linux/brcm47xx/patches-3.6/548-bcma-register-watchdog-driver.patch
new file mode 100644
index 000000000..457df36d8
--- /dev/null
+++ b/target/linux/brcm47xx/patches-3.6/548-bcma-register-watchdog-driver.patch
@@ -0,0 +1,92 @@
+--- a/drivers/bcma/bcma_private.h
++++ b/drivers/bcma/bcma_private.h
+@@ -85,6 +85,8 @@ extern void __exit bcma_host_pci_exit(vo
+ /* 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);
+--- 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_pe
+ }
+ }
+
++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)
+--- a/drivers/bcma/main.c
++++ b/drivers/bcma/main.c
+@@ -173,6 +173,12 @@ static int bcma_register_cores(struct bc
+ }
+ #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;
+ }
+
+@@ -185,6 +191,8 @@ static void bcma_unregister_cores(struct
+ 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)
+--- a/include/linux/bcma/bcma_driver_chipcommon.h
++++ b/include/linux/bcma/bcma_driver_chipcommon.h
+@@ -4,6 +4,8 @@
+ #include <linux/mtd/bcm47xx_sflash.h>
+ #include <linux/mtd/bcm47xx_nand.h>
+
++#include <linux/platform_device.h>
++
+ /** ChipCommon core registers. **/
+ #define BCMA_CC_ID 0x0000
+ #define BCMA_CC_ID_ID 0x0000FFFF
+@@ -555,6 +557,7 @@ struct bcma_drv_cc {
+ /* Lock for GPIO register access. */
+ spinlock_t gpio_lock;
+ u32 ticks_per_ms;
++ struct platform_device *watchdog;
+ };
+
+ /* Register access */
diff --git a/target/linux/brcm47xx/patches-3.6/549-ssb-get-alp-clock-from-devices-with-PMU.patch b/target/linux/brcm47xx/patches-3.6/549-ssb-get-alp-clock-from-devices-with-PMU.patch
new file mode 100644
index 000000000..f5912984d
--- /dev/null
+++ b/target/linux/brcm47xx/patches-3.6/549-ssb-get-alp-clock-from-devices-with-PMU.patch
@@ -0,0 +1,76 @@
+--- a/drivers/ssb/driver_chipcommon.c
++++ b/drivers/ssb/driver_chipcommon.c
+@@ -280,6 +280,14 @@ static void calc_fast_powerup_delay(stru
+ 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_ch
+ | 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. */
+--- 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_ch
+ 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;
+--- a/drivers/ssb/ssb_private.h
++++ b/drivers/ssb/ssb_private.h
+@@ -210,6 +210,7 @@ static inline void b43_pci_ssb_bridge_ex
+ /* 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);
+
+ #ifdef CONFIG_SSB_SFLASH
+ /* driver_chipcommon_sflash.c */
diff --git a/target/linux/brcm47xx/patches-3.6/550-ssb-set-the-pmu-watchdog-if-available.patch b/target/linux/brcm47xx/patches-3.6/550-ssb-set-the-pmu-watchdog-if-available.patch
new file mode 100644
index 000000000..f7cc07bcd
--- /dev/null
+++ b/target/linux/brcm47xx/patches-3.6/550-ssb-set-the-pmu-watchdog-if-available.patch
@@ -0,0 +1,54 @@
+--- a/drivers/ssb/driver_chipcommon.c
++++ b/drivers/ssb/driver_chipcommon.c
+@@ -288,6 +288,24 @@ static u32 ssb_chipco_alp_clock(struct s
+ 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_c
+ /* 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)
diff --git a/target/linux/brcm47xx/patches-3.6/551-ssb-add-methods-for-watchdog-driver.patch b/target/linux/brcm47xx/patches-3.6/551-ssb-add-methods-for-watchdog-driver.patch
new file mode 100644
index 000000000..46911d130
--- /dev/null
+++ b/target/linux/brcm47xx/patches-3.6/551-ssb-add-methods-for-watchdog-driver.patch
@@ -0,0 +1,131 @@
+--- 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 <m@bues.ch>
++ * Copyright 2012, Hauke Mehrtens <hauke@hauke-m.de>
+ *
+ * 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_t
+ 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_chip
+ 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_c
+ }
+
+ /* 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(struc
+ /* instant NMI */
+ chipco_write32(cc, SSB_CHIPCO_WATCHDOG, ticks);
+ }
++ return ticks;
+ }
+
+ void ssb_chipco_irq_mask(struct ssb_chipcommon *cc, u32 mask, u32 value)
+--- 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: "
+@@ -226,4 +227,8 @@ static inline int ssb_sflash_init(struct
+
+ extern struct platform_device ssb_pflash_dev;
+
++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_ */
+--- a/include/linux/ssb/ssb_driver_chipcommon.h
++++ b/include/linux/ssb/ssb_driver_chipcommon.h
+@@ -607,6 +607,8 @@ struct ssb_chipcommon {
+ #ifdef CONFIG_SSB_SFLASH
+ struct bcm47xx_sflash sflash;
+ #endif
++ u32 ticks_per_ms;
++ u32 max_timer_ms;
+ };
+
+ static inline bool ssb_chipco_available(struct ssb_chipcommon *cc)
+@@ -646,8 +648,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);
+
diff --git a/target/linux/brcm47xx/patches-3.6/552-ssb-extif-add-check-for-max-value-in-watchdog.patch b/target/linux/brcm47xx/patches-3.6/552-ssb-extif-add-check-for-max-value-in-watchdog.patch
new file mode 100644
index 000000000..c6756dd11
--- /dev/null
+++ b/target/linux/brcm47xx/patches-3.6/552-ssb-extif-add-check-for-max-value-in-watchdog.patch
@@ -0,0 +1,25 @@
+--- a/drivers/ssb/driver_extif.c
++++ b/drivers/ssb/driver_extif.c
+@@ -112,9 +112,10 @@ void ssb_extif_get_clockcontrol(struct s
+ *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);
+ }
+
+--- 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
diff --git a/target/linux/brcm47xx/patches-3.6/553-ssb-extif-add-methods-for-watchdog-driver.patch b/target/linux/brcm47xx/patches-3.6/553-ssb-extif-add-methods-for-watchdog-driver.patch
new file mode 100644
index 000000000..555cf64db
--- /dev/null
+++ b/target/linux/brcm47xx/patches-3.6/553-ssb-extif-add-methods-for-watchdog-driver.patch
@@ -0,0 +1,89 @@
+--- a/drivers/ssb/driver_extif.c
++++ b/drivers/ssb/driver_extif.c
+@@ -112,11 +112,30 @@ void ssb_extif_get_clockcontrol(struct s
+ *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)
+--- a/drivers/ssb/ssb_private.h
++++ b/drivers/ssb/ssb_private.h
+@@ -231,4 +231,19 @@ extern u32 ssb_chipco_watchdog_timer_set
+ 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_ */
+--- 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(s
+ 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 s
+ }
+
+ 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 */
diff --git a/target/linux/brcm47xx/patches-3.6/554-ssb-register-watchdog-driver.patch b/target/linux/brcm47xx/patches-3.6/554-ssb-register-watchdog-driver.patch
new file mode 100644
index 000000000..c814c54ac
--- /dev/null
+++ b/target/linux/brcm47xx/patches-3.6/554-ssb-register-watchdog-driver.patch
@@ -0,0 +1,122 @@
+--- a/drivers/ssb/embedded.c
++++ b/drivers/ssb/embedded.c
+@@ -4,11 +4,13 @@
+ *
+ * Copyright 2005-2008, Broadcom Corporation
+ * Copyright 2006-2008, Michael Buesch <m@bues.ch>
++ * Copyright 2012, Hauke Mehrtens <hauke@hauke-m.de>
+ *
+ * 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_bu
+ }
+ 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;
+--- 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>
+@@ -434,6 +435,11 @@ static void ssb_devices_unregister(struc
+ 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)
+@@ -579,6 +585,8 @@ static int __devinit ssb_attach_queued_b
+ 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);
+--- a/drivers/ssb/ssb_private.h
++++ b/drivers/ssb/ssb_private.h
+@@ -246,4 +246,14 @@ static inline u32 ssb_extif_watchdog_tim
+ 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_ */
+--- 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. */
diff --git a/target/linux/brcm47xx/patches-3.6/900-bcm47xx_wdt-noprescale.patch b/target/linux/brcm47xx/patches-3.6/900-bcm47xx_wdt-noprescale.patch
deleted file mode 100644
index bf0a19268..000000000
--- a/target/linux/brcm47xx/patches-3.6/900-bcm47xx_wdt-noprescale.patch
+++ /dev/null
@@ -1,103 +0,0 @@
---- a/drivers/watchdog/bcm47xx_wdt.c
-+++ b/drivers/watchdog/bcm47xx_wdt.c
-@@ -33,6 +33,7 @@
-
- #define WDT_DEFAULT_TIME 30 /* seconds */
- #define WDT_MAX_TIME 255 /* seconds */
-+#define WDT_SHIFT 15 /* 32.768 KHz on cores with slow WDT clock */
-
- static int wdt_time = WDT_DEFAULT_TIME;
- static bool nowayout = WATCHDOG_NOWAYOUT;
-@@ -52,20 +53,20 @@ static unsigned long bcm47xx_wdt_busy;
- static char expect_release;
- static struct timer_list wdt_timer;
- static atomic_t ticks;
-+static int needs_sw_scale;
-
--static inline void bcm47xx_wdt_hw_start(void)
-+static inline void bcm47xx_wdt_hw_start(u32 ticks)
- {
-- /* 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);
-+ ssb_watchdog_timer_set(&bcm47xx_bus.ssb, ticks);
- break;
- #endif
- #ifdef CONFIG_BCM47XX_BCMA
- case BCM47XX_BUS_TYPE_BCMA:
- bcma_chipco_watchdog_timer_set(&bcm47xx_bus.bcma.bus.drv_cc,
-- 0xfffffff);
-+ ticks);
- break;
- #endif
- }
-@@ -90,33 +91,34 @@ static inline int bcm47xx_wdt_hw_stop(vo
- static void bcm47xx_timer_tick(unsigned long unused)
- {
- if (!atomic_dec_and_test(&ticks)) {
-- bcm47xx_wdt_hw_start();
-+ /* This is 2,5s on 100Mhz clock and 2s on 133 Mhz */
-+ bcm47xx_wdt_hw_start(0xfffffff);
- mod_timer(&wdt_timer, jiffies + HZ);
- } else {
- pr_crit("Watchdog will fire soon!!!\n");
- }
- }
-
--static inline void bcm47xx_wdt_pet(void)
-+static void bcm47xx_wdt_pet(void)
- {
-- atomic_set(&ticks, wdt_time);
-+ if(needs_sw_scale)
-+ atomic_set(&ticks, wdt_time);
-+ else
-+ bcm47xx_wdt_hw_start(wdt_time << WDT_SHIFT);
- }
-
- static void bcm47xx_wdt_start(void)
- {
- bcm47xx_wdt_pet();
-- bcm47xx_timer_tick(0);
--}
--
--static void bcm47xx_wdt_pause(void)
--{
-- del_timer_sync(&wdt_timer);
-- bcm47xx_wdt_hw_stop();
-+ if(needs_sw_scale)
-+ bcm47xx_timer_tick(0);
- }
-
- static void bcm47xx_wdt_stop(void)
- {
-- bcm47xx_wdt_pause();
-+ if(needs_sw_scale)
-+ del_timer_sync(&wdt_timer);
-+ bcm47xx_wdt_hw_stop();
- }
-
- static int bcm47xx_wdt_settimeout(int new_time)
-@@ -267,7 +269,20 @@ static int __init bcm47xx_wdt_init(void)
- if (bcm47xx_wdt_hw_stop() < 0)
- return -ENODEV;
-
-- setup_timer(&wdt_timer, bcm47xx_timer_tick, 0L);
-+ /* FIXME Other cores */
-+#ifdef BCM47XX_BUS_TYPE_BCMA
-+ if(bcm47xx_bus_type == BCM47XX_BUS_TYPE_BCMA &&
-+ bcm47xx_bus.ssb.chip_id == 0x5354) {
-+ /* Slow WDT clock, no pre-scaling */
-+ needs_sw_scale = 0;
-+ } else {
-+#endif
-+ /* Fast WDT clock, needs software pre-scaling */
-+ needs_sw_scale = 1;
-+ setup_timer(&wdt_timer, bcm47xx_timer_tick, 0L);
-+#ifdef BCM47XX_BUS_TYPE_BCMA
-+ }
-+#endif
-
- if (bcm47xx_wdt_settimeout(wdt_time)) {
- bcm47xx_wdt_settimeout(WDT_DEFAULT_TIME);