add new switch configuration api
[openwrt/staging/chunkeey.git] / target / linux / generic-2.6 / files / drivers / ssb / pcmcia.c
index b9085633ebfbbe4bec9a177ed2b68ba5ea6e71bc..46816cda8b984411da0e9aceeb40baaab82f9c8c 100644 (file)
@@ -10,6 +10,7 @@
 
 #include <linux/ssb/ssb.h>
 #include <linux/delay.h>
+#include <linux/io.h>
 
 #include <pcmcia/cs_types.h>
 #include <pcmcia/cs.h>
 #include "ssb_private.h"
 
 
+/* Define the following to 1 to enable a printk on each coreswitch. */
+#define SSB_VERBOSE_PCMCIACORESWITCH_DEBUG             0
+
+
 int ssb_pcmcia_switch_coreidx(struct ssb_bus *bus,
                              u8 coreidx)
 {
@@ -58,17 +63,17 @@ int ssb_pcmcia_switch_coreidx(struct ssb_bus *bus,
                err = pcmcia_access_configuration_register(pdev, &reg);
                if (err != CS_SUCCESS)
                        goto error;
-               read_addr |= (reg.Value & 0xF) << 12;
+               read_addr |= ((u32)(reg.Value & 0x0F)) << 12;
                reg.Offset = 0x30;
                err = pcmcia_access_configuration_register(pdev, &reg);
                if (err != CS_SUCCESS)
                        goto error;
-               read_addr |= reg.Value << 16;
+               read_addr |= ((u32)reg.Value) << 16;
                reg.Offset = 0x32;
                err = pcmcia_access_configuration_register(pdev, &reg);
                if (err != CS_SUCCESS)
                        goto error;
-               read_addr |= reg.Value << 24;
+               read_addr |= ((u32)reg.Value) << 24;
 
                cur_core = (read_addr - SSB_ENUM_BASE) / SSB_CORE_SIZE;
                if (cur_core == coreidx)
@@ -89,18 +94,17 @@ int ssb_pcmcia_switch_core(struct ssb_bus *bus,
                           struct ssb_device *dev)
 {
        int err;
-       unsigned long flags;
 
-       ssb_dprintk(KERN_INFO PFX
-                   "Switching to %s core, index %d\n",
-                   ssb_core_name(dev->id.coreid),
-                   dev->core_index);
+#if SSB_VERBOSE_PCMCIACORESWITCH_DEBUG
+       ssb_printk(KERN_INFO PFX
+                  "Switching to %s core, index %d\n",
+                  ssb_core_name(dev->id.coreid),
+                  dev->core_index);
+#endif
 
-       spin_lock_irqsave(&bus->bar_lock, flags);
        err = ssb_pcmcia_switch_coreidx(bus, dev->core_index);
        if (!err)
                bus->mapped_device = dev;
-       spin_unlock_irqrestore(&bus->bar_lock, flags);
 
        return err;
 }
@@ -108,14 +112,12 @@ int ssb_pcmcia_switch_core(struct ssb_bus *bus,
 int ssb_pcmcia_switch_segment(struct ssb_bus *bus, u8 seg)
 {
        int attempts = 0;
-       unsigned long flags;
        conf_reg_t reg;
-       int res, err = 0;
+       int res;
 
-       assert(seg == 0 || seg == 1);
+       SSB_WARN_ON((seg != 0) && (seg != 1));
        reg.Offset = 0x34;
        reg.Function = 0;
-       spin_lock_irqsave(&bus->bar_lock, flags);
        while (1) {
                reg.Action = CS_WRITE;
                reg.Value = seg;
@@ -136,34 +138,36 @@ int ssb_pcmcia_switch_segment(struct ssb_bus *bus, u8 seg)
                udelay(10);
        }
        bus->mapped_pcmcia_seg = seg;
-out_unlock:
-       spin_unlock_irqrestore(&bus->bar_lock, flags);
-       return err;
+
+       return 0;
 error:
        ssb_printk(KERN_ERR PFX "Failed to switch pcmcia segment\n");
-       err = -ENODEV;
-       goto out_unlock;
+       return -ENODEV;
 }
 
-static inline int do_select_core(struct ssb_bus *bus,
-                                struct ssb_device *dev,
-                                u16 *offset)
+static int select_core_and_segment(struct ssb_device *dev,
+                                  u16 *offset)
 {
+       struct ssb_bus *bus = dev->bus;
        int err;
-       u8 need_seg = (*offset >= 0x800) ? 1 : 0;
+       u8 need_segment;
+
+       if (*offset >= 0x800) {
+               *offset -= 0x800;
+               need_segment = 1;
+       } else
+               need_segment = 0;
 
        if (unlikely(dev != bus->mapped_device)) {
                err = ssb_pcmcia_switch_core(bus, dev);
                if (unlikely(err))
                        return err;
        }
-       if (unlikely(need_seg != bus->mapped_pcmcia_seg)) {
-               err = ssb_pcmcia_switch_segment(bus, need_seg);
+       if (unlikely(need_segment != bus->mapped_pcmcia_seg)) {
+               err = ssb_pcmcia_switch_segment(bus, need_segment);
                if (unlikely(err))
                        return err;
        }
-       if (need_seg == 1)
-               *offset -= 0x800;
 
        return 0;
 }
@@ -171,50 +175,68 @@ static inline int do_select_core(struct ssb_bus *bus,
 static u16 ssb_pcmcia_read16(struct ssb_device *dev, u16 offset)
 {
        struct ssb_bus *bus = dev->bus;
-       u16 x;
+       unsigned long flags;
+       int err;
+       u16 value = 0xFFFF;
 
-       if (unlikely(do_select_core(bus, dev, &offset)))
-               return 0xFFFF;
-       x = readw(bus->mmio + offset);
-//printk("R16 0x%04X, 0x%04X\n", offset, x);
-       return x;
+       spin_lock_irqsave(&bus->bar_lock, flags);
+       err = select_core_and_segment(dev, &offset);
+       if (likely(!err))
+               value = readw(bus->mmio + offset);
+       spin_unlock_irqrestore(&bus->bar_lock, flags);
+
+       return value;
 }
 
 static u32 ssb_pcmcia_read32(struct ssb_device *dev, u16 offset)
 {
        struct ssb_bus *bus = dev->bus;
-       u32 x;
+       unsigned long flags;
+       int err;
+       u32 lo = 0xFFFFFFFF, hi = 0xFFFFFFFF;
+
+       spin_lock_irqsave(&bus->bar_lock, flags);
+       err = select_core_and_segment(dev, &offset);
+       if (likely(!err)) {
+               lo = readw(bus->mmio + offset);
+               hi = readw(bus->mmio + offset + 2);
+       }
+       spin_unlock_irqrestore(&bus->bar_lock, flags);
 
-       if (unlikely(do_select_core(bus, dev, &offset)))
-               return 0xFFFFFFFF;
-       x = readl(bus->mmio + offset);
-//printk("R32 0x%04X, 0x%08X\n", offset, x);
-       return x;
+       return (lo | (hi << 16));
 }
 
 static void ssb_pcmcia_write16(struct ssb_device *dev, u16 offset, u16 value)
 {
        struct ssb_bus *bus = dev->bus;
+       unsigned long flags;
+       int err;
 
-       if (unlikely(do_select_core(bus, dev, &offset)))
-               return;
-//printk("W16 0x%04X, 0x%04X\n", offset, value);
-       writew(value, bus->mmio + offset);
+       spin_lock_irqsave(&bus->bar_lock, flags);
+       err = select_core_and_segment(dev, &offset);
+       if (likely(!err))
+               writew(value, bus->mmio + offset);
+       mmiowb();
+       spin_unlock_irqrestore(&bus->bar_lock, flags);
 }
 
 static void ssb_pcmcia_write32(struct ssb_device *dev, u16 offset, u32 value)
 {
        struct ssb_bus *bus = dev->bus;
+       unsigned long flags;
+       int err;
 
-       if (unlikely(do_select_core(bus, dev, &offset)))
-               return;
-//printk("W32 0x%04X, 0x%08X\n", offset, value);
-       readw(bus->mmio + offset);
-       writew(value >> 16, bus->mmio + offset + 2);
-       readw(bus->mmio + offset);
-       writew(value, bus->mmio + offset);
+       spin_lock_irqsave(&bus->bar_lock, flags);
+       err = select_core_and_segment(dev, &offset);
+       if (likely(!err)) {
+               writew((value & 0x0000FFFF), bus->mmio + offset);
+               writew(((value & 0xFFFF0000) >> 16), bus->mmio + offset + 2);
+       }
+       mmiowb();
+       spin_unlock_irqrestore(&bus->bar_lock, flags);
 }
 
+/* Not "static", as it's used in main.c */
 const struct ssb_bus_ops ssb_pcmcia_ops = {
        .read16         = ssb_pcmcia_read16,
        .read32         = ssb_pcmcia_read32,
@@ -222,10 +244,12 @@ const struct ssb_bus_ops ssb_pcmcia_ops = {
        .write32        = ssb_pcmcia_write32,
 };
 
+#include <linux/etherdevice.h>
 int ssb_pcmcia_get_invariants(struct ssb_bus *bus,
                              struct ssb_init_invariants *iv)
 {
        //TODO
+       random_ether_addr(iv->sprom.il0mac);
        return 0;
 }