diff options
author | hauke <hauke@3c298f89-4303-0410-b956-a3cf2f4a3e73> | 2012-11-24 20:07:25 +0000 |
---|---|---|
committer | hauke <hauke@3c298f89-4303-0410-b956-a3cf2f4a3e73> | 2012-11-24 20:07:25 +0000 |
commit | 9a462204d13bd8acd735f8111dc7f9412e812b33 (patch) | |
tree | 90fc287e9ed841c2ec2972694366a16375b511fd /target/linux | |
parent | 0b36fa881c11ebe7deae587bd6cae851d70f7477 (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/linux')
17 files changed, 1566 insertions, 103 deletions
diff --git a/target/linux/brcm47xx/config-3.6 b/target/linux/brcm47xx/config-3.6 index 278e58cf0f..4859a5b6d1 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 0000000000..7ac56387fe --- /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 0000000000..79bb962f78 --- /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 0000000000..54956fd2d7 --- /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 0000000000..c08f0b1536 --- /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 0000000000..3e67ba2bb1 --- /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 0000000000..f6f0012a7a --- /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 0000000000..651415eb9b --- /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 0000000000..1be2476d39 --- /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 0000000000..457df36d8d --- /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 0000000000..f5912984d7 --- /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 0000000000..f7cc07bcd7 --- /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 0000000000..46911d1306 --- /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 0000000000..c6756dd11a --- /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 0000000000..555cf64dbd --- /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 0000000000..c814c54ac4 --- /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 bf0a192684..0000000000 --- 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); |