m68k: fix __page_to_pfn for a const struct page argument
[linux-2.6.git] / drivers / ssb / driver_chipcommon.c
index 7cc03f2..5d9c97c 100644 (file)
@@ -3,7 +3,7 @@
  * Broadcom ChipCommon core driver
  *
  * Copyright 2005, Broadcom Corporation
- * Copyright 2006, 2007, Michael Buesch <mb@bu3sch.de>
+ * Copyright 2006, 2007, Michael Buesch <m@bues.ch>
  *
  * Licensed under the GNU/GPL. See COPYING for details.
  */
@@ -26,25 +26,14 @@ enum ssb_clksrc {
 };
 
 
-static inline u32 chipco_read32(struct ssb_chipcommon *cc,
-                               u16 offset)
-{
-       return ssb_read32(cc->dev, offset);
-}
-
-static inline void chipco_write32(struct ssb_chipcommon *cc,
-                                 u16 offset,
-                                 u32 value)
-{
-       ssb_write32(cc->dev, offset, value);
-}
-
-static inline void chipco_write32_masked(struct ssb_chipcommon *cc, u16 offset,
-                                        u32 mask, u32 value)
+static inline u32 chipco_write32_masked(struct ssb_chipcommon *cc, u16 offset,
+                                       u32 mask, u32 value)
 {
        value &= mask;
        value |= chipco_read32(cc, offset) & ~mask;
        chipco_write32(cc, offset, value);
+
+       return value;
 }
 
 void ssb_chipco_set_clockmode(struct ssb_chipcommon *cc,
@@ -57,40 +46,66 @@ void ssb_chipco_set_clockmode(struct ssb_chipcommon *cc,
        if (!ccdev)
                return;
        bus = ccdev->bus;
+
+       /* We support SLOW only on 6..9 */
+       if (ccdev->id.revision >= 10 && mode == SSB_CLKMODE_SLOW)
+               mode = SSB_CLKMODE_DYNAMIC;
+
+       if (cc->capabilities & SSB_CHIPCO_CAP_PMU)
+               return; /* PMU controls clockmode, separated function needed */
+       SSB_WARN_ON(ccdev->id.revision >= 20);
+
        /* chipcommon cores prior to rev6 don't support dynamic clock control */
        if (ccdev->id.revision < 6)
                return;
-       /* chipcommon cores rev10 are a whole new ball game */
+
+       /* ChipCommon cores rev10+ need testing */
        if (ccdev->id.revision >= 10)
                return;
+
        if (!(cc->capabilities & SSB_CHIPCO_CAP_PCTL))
                return;
 
        switch (mode) {
-       case SSB_CLKMODE_SLOW:
+       case SSB_CLKMODE_SLOW: /* For revs 6..9 only */
                tmp = chipco_read32(cc, SSB_CHIPCO_SLOWCLKCTL);
                tmp |= SSB_CHIPCO_SLOWCLKCTL_FSLOW;
                chipco_write32(cc, SSB_CHIPCO_SLOWCLKCTL, tmp);
                break;
        case SSB_CLKMODE_FAST:
-               ssb_pci_xtal(bus, SSB_GPIO_XTAL, 1); /* Force crystal on */
-               tmp = chipco_read32(cc, SSB_CHIPCO_SLOWCLKCTL);
-               tmp &= ~SSB_CHIPCO_SLOWCLKCTL_FSLOW;
-               tmp |= SSB_CHIPCO_SLOWCLKCTL_IPLL;
-               chipco_write32(cc, SSB_CHIPCO_SLOWCLKCTL, tmp);
+               if (ccdev->id.revision < 10) {
+                       ssb_pci_xtal(bus, SSB_GPIO_XTAL, 1); /* Force crystal on */
+                       tmp = chipco_read32(cc, SSB_CHIPCO_SLOWCLKCTL);
+                       tmp &= ~SSB_CHIPCO_SLOWCLKCTL_FSLOW;
+                       tmp |= SSB_CHIPCO_SLOWCLKCTL_IPLL;
+                       chipco_write32(cc, SSB_CHIPCO_SLOWCLKCTL, tmp);
+               } else {
+                       chipco_write32(cc, SSB_CHIPCO_SYSCLKCTL,
+                               (chipco_read32(cc, SSB_CHIPCO_SYSCLKCTL) |
+                                SSB_CHIPCO_SYSCLKCTL_FORCEHT));
+                       /* udelay(150); TODO: not available in early init */
+               }
                break;
        case SSB_CLKMODE_DYNAMIC:
-               tmp = chipco_read32(cc, SSB_CHIPCO_SLOWCLKCTL);
-               tmp &= ~SSB_CHIPCO_SLOWCLKCTL_FSLOW;
-               tmp &= ~SSB_CHIPCO_SLOWCLKCTL_IPLL;
-               tmp &= ~SSB_CHIPCO_SLOWCLKCTL_ENXTAL;
-               if ((tmp & SSB_CHIPCO_SLOWCLKCTL_SRC) != SSB_CHIPCO_SLOWCLKCTL_SRC_XTAL)
-                       tmp |= SSB_CHIPCO_SLOWCLKCTL_ENXTAL;
-               chipco_write32(cc, SSB_CHIPCO_SLOWCLKCTL, tmp);
-
-               /* for dynamic control, we have to release our xtal_pu "force on" */
-               if (tmp & SSB_CHIPCO_SLOWCLKCTL_ENXTAL)
-                       ssb_pci_xtal(bus, SSB_GPIO_XTAL, 0);
+               if (ccdev->id.revision < 10) {
+                       tmp = chipco_read32(cc, SSB_CHIPCO_SLOWCLKCTL);
+                       tmp &= ~SSB_CHIPCO_SLOWCLKCTL_FSLOW;
+                       tmp &= ~SSB_CHIPCO_SLOWCLKCTL_IPLL;
+                       tmp &= ~SSB_CHIPCO_SLOWCLKCTL_ENXTAL;
+                       if ((tmp & SSB_CHIPCO_SLOWCLKCTL_SRC) !=
+                           SSB_CHIPCO_SLOWCLKCTL_SRC_XTAL)
+                               tmp |= SSB_CHIPCO_SLOWCLKCTL_ENXTAL;
+                       chipco_write32(cc, SSB_CHIPCO_SLOWCLKCTL, tmp);
+
+                       /* For dynamic control, we have to release our xtal_pu
+                        * "force on" */
+                       if (tmp & SSB_CHIPCO_SLOWCLKCTL_ENXTAL)
+                               ssb_pci_xtal(bus, SSB_GPIO_XTAL, 0);
+               } else {
+                       chipco_write32(cc, SSB_CHIPCO_SYSCLKCTL,
+                               (chipco_read32(cc, SSB_CHIPCO_SYSCLKCTL) &
+                                ~SSB_CHIPCO_SYSCLKCTL_FORCEHT));
+               }
                break;
        default:
                SSB_WARN_ON(1);
@@ -220,6 +235,24 @@ static void chipco_powercontrol_init(struct ssb_chipcommon *cc)
        }
 }
 
+/* http://bcm-v4.sipsolutions.net/802.11/PmuFastPwrupDelay */
+static u16 pmu_fast_powerup_delay(struct ssb_chipcommon *cc)
+{
+       struct ssb_bus *bus = cc->dev->bus;
+
+       switch (bus->chip_id) {
+       case 0x4312:
+       case 0x4322:
+       case 0x4328:
+               return 7000;
+       case 0x4325:
+               /* TODO: */
+       default:
+               return 15000;
+       }
+}
+
+/* http://bcm-v4.sipsolutions.net/802.11/ClkctlFastPwrupDelay */
 static void calc_fast_powerup_delay(struct ssb_chipcommon *cc)
 {
        struct ssb_bus *bus = cc->dev->bus;
@@ -229,6 +262,12 @@ static void calc_fast_powerup_delay(struct ssb_chipcommon *cc)
 
        if (bus->bustype != SSB_BUSTYPE_PCI)
                return;
+
+       if (cc->capabilities & SSB_CHIPCO_CAP_PMU) {
+               cc->fast_pwrup_delay = pmu_fast_powerup_delay(cc);
+               return;
+       }
+
        if (!(cc->capabilities & SSB_CHIPCO_CAP_PCTL))
                return;
 
@@ -244,12 +283,22 @@ void ssb_chipcommon_init(struct ssb_chipcommon *cc)
 {
        if (!cc->dev)
                return; /* We don't have a ChipCommon */
+       if (cc->dev->id.revision >= 11)
+               cc->status = chipco_read32(cc, SSB_CHIPCO_CHIPSTAT);
+       ssb_dprintk(KERN_INFO PFX "chipcommon status is 0x%x\n", cc->status);
+
+       if (cc->dev->id.revision >= 20) {
+               chipco_write32(cc, SSB_CHIPCO_GPIOPULLUP, 0);
+               chipco_write32(cc, SSB_CHIPCO_GPIOPULLDOWN, 0);
+       }
+
+       ssb_pmu_init(cc);
        chipco_powercontrol_init(cc);
        ssb_chipco_set_clockmode(cc, SSB_CLKMODE_FAST);
        calc_fast_powerup_delay(cc);
 }
 
-void ssb_chipco_suspend(struct ssb_chipcommon *cc, pm_message_t state)
+void ssb_chipco_suspend(struct ssb_chipcommon *cc)
 {
        if (!cc->dev)
                return;
@@ -351,19 +400,45 @@ void ssb_chipco_watchdog_timer_set(struct ssb_chipcommon *cc, u32 ticks)
        chipco_write32(cc, SSB_CHIPCO_WATCHDOG, ticks);
 }
 
+void ssb_chipco_irq_mask(struct ssb_chipcommon *cc, u32 mask, u32 value)
+{
+       chipco_write32_masked(cc, SSB_CHIPCO_IRQMASK, mask, value);
+}
+
+u32 ssb_chipco_irq_status(struct ssb_chipcommon *cc, u32 mask)
+{
+       return chipco_read32(cc, SSB_CHIPCO_IRQSTAT) & mask;
+}
+
 u32 ssb_chipco_gpio_in(struct ssb_chipcommon *cc, u32 mask)
 {
        return chipco_read32(cc, SSB_CHIPCO_GPIOIN) & mask;
 }
 
-void ssb_chipco_gpio_out(struct ssb_chipcommon *cc, u32 mask, u32 value)
+u32 ssb_chipco_gpio_out(struct ssb_chipcommon *cc, u32 mask, u32 value)
+{
+       return chipco_write32_masked(cc, SSB_CHIPCO_GPIOOUT, mask, value);
+}
+
+u32 ssb_chipco_gpio_outen(struct ssb_chipcommon *cc, u32 mask, u32 value)
+{
+       return chipco_write32_masked(cc, SSB_CHIPCO_GPIOOUTEN, mask, value);
+}
+
+u32 ssb_chipco_gpio_control(struct ssb_chipcommon *cc, u32 mask, u32 value)
+{
+       return chipco_write32_masked(cc, SSB_CHIPCO_GPIOCTL, mask, value);
+}
+EXPORT_SYMBOL(ssb_chipco_gpio_control);
+
+u32 ssb_chipco_gpio_intmask(struct ssb_chipcommon *cc, u32 mask, u32 value)
 {
-       chipco_write32_masked(cc, SSB_CHIPCO_GPIOOUT, mask, value);
+       return chipco_write32_masked(cc, SSB_CHIPCO_GPIOIRQ, mask, value);
 }
 
-void ssb_chipco_gpio_outen(struct ssb_chipcommon *cc, u32 mask, u32 value)
+u32 ssb_chipco_gpio_polarity(struct ssb_chipcommon *cc, u32 mask, u32 value)
 {
-       chipco_write32_masked(cc, SSB_CHIPCO_GPIOOUTEN, mask, value);
+       return chipco_write32_masked(cc, SSB_CHIPCO_GPIOPOL, mask, value);
 }
 
 #ifdef CONFIG_SSB_SERIAL