summaryrefslogtreecommitdiff
path: root/drivers/bcma/driver_chipcommon.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/bcma/driver_chipcommon.c')
-rw-r--r--drivers/bcma/driver_chipcommon.c135
1 files changed, 126 insertions, 9 deletions
diff --git a/drivers/bcma/driver_chipcommon.c b/drivers/bcma/driver_chipcommon.c
index a4c3ebcc4c86..dc96dd8ebff2 100644
--- a/drivers/bcma/driver_chipcommon.c
+++ b/drivers/bcma/driver_chipcommon.c
@@ -4,12 +4,15 @@
*
* 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.
*/
#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,
@@ -22,12 +25,93 @@ static inline u32 bcma_cc_write32_masked(struct bcma_drv_cc *cc, u16 offset,
return value;
}
-void bcma_core_chipcommon_init(struct bcma_drv_cc *cc)
+static u32 bcma_chipco_get_alp_clock(struct bcma_drv_cc *cc)
{
- u32 leddc_on = 10;
- u32 leddc_off = 90;
+ if (cc->capabilities & BCMA_CC_CAP_PMU)
+ return bcma_pmu_get_alp_clock(cc);
- if (cc->setup_done)
+ 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;
+}
+
+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_get_alp_clock(cc) / 4000;
+ else
+ /* based on 32KHz ILP clock */
+ return 32;
+ } else {
+ return bcma_chipco_get_alp_clock(cc) / 1000;
+ }
+}
+
+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)
return;
if (cc->core->id.rev >= 11)
@@ -36,6 +120,22 @@ void bcma_core_chipcommon_init(struct bcma_drv_cc *cc)
if (cc->core->id.rev >= 35)
cc->capabilities_ext = bcma_cc_read32(cc, BCMA_CC_CAP_EXT);
+ if (cc->capabilities & BCMA_CC_CAP_PMU)
+ bcma_pmu_early_init(cc);
+
+ cc->early_setup_done = true;
+}
+
+void bcma_core_chipcommon_init(struct bcma_drv_cc *cc)
+{
+ u32 leddc_on = 10;
+ u32 leddc_off = 90;
+
+ if (cc->setup_done)
+ return;
+
+ bcma_core_chipcommon_early_init(cc);
+
if (cc->core->id.rev >= 20) {
bcma_cc_write32(cc, BCMA_CC_GPIOPULLUP, 0);
bcma_cc_write32(cc, BCMA_CC_GPIOPULLDOWN, 0);
@@ -56,15 +156,33 @@ void bcma_core_chipcommon_init(struct bcma_drv_cc *cc)
((leddc_on << BCMA_CC_GPIOTIMER_ONTIME_SHIFT) |
(leddc_off << BCMA_CC_GPIOTIMER_OFFTIME_SHIFT)));
}
+ cc->ticks_per_ms = bcma_chipco_watchdog_ticks_per_ms(cc);
cc->setup_done = true;
}
/* Set chip watchdog reset timer to fire in 'ticks' backplane cycles */
-void bcma_chipco_watchdog_timer_set(struct bcma_drv_cc *cc, u32 ticks)
+u32 bcma_chipco_watchdog_timer_set(struct bcma_drv_cc *cc, u32 ticks)
{
- /* instant NMI */
- bcma_cc_write32(cc, BCMA_CC_WATCHDOG, ticks);
+ u32 maxt;
+ enum bcma_clkmode clkmode;
+
+ maxt = bcma_chipco_watchdog_get_max_timer(cc);
+ if (cc->capabilities & BCMA_CC_CAP_PMU) {
+ if (ticks == 1)
+ ticks = 2;
+ else if (ticks > maxt)
+ ticks = maxt;
+ bcma_cc_write32(cc, BCMA_CC_PMU_WATCHDOG, ticks);
+ } else {
+ clkmode = ticks ? BCMA_CLKMODE_FAST : BCMA_CLKMODE_DYNAMIC;
+ bcma_core_set_clockmode(cc->core, clkmode);
+ if (ticks > maxt)
+ ticks = maxt;
+ /* instant NMI */
+ bcma_cc_write32(cc, BCMA_CC_WATCHDOG, ticks);
+ }
+ return ticks;
}
void bcma_chipco_irq_mask(struct bcma_drv_cc *cc, u32 mask, u32 value)
@@ -118,8 +236,7 @@ void bcma_chipco_serial_init(struct bcma_drv_cc *cc)
struct bcma_serial_port *ports = cc->serial_ports;
if (ccrev >= 11 && ccrev != 15) {
- /* Fixed ALP clock */
- baud_base = bcma_pmu_alp_clock(cc);
+ baud_base = bcma_chipco_get_alp_clock(cc);
if (ccrev >= 21) {
/* Turn off UART clock before switching clocksource. */
bcma_cc_write32(cc, BCMA_CC_CORECTL,