ssb: Fix PCMCIA lowlevel register access

This fixes lowlevel register access for PCMCIA based devices.

The patch also adds a temporary workaround for the device mac address.
It simply adds generation of a random address. The real SPROM extraction
will follow in another patch.
The temporary workaround will be removed then, but for now it's OK.

Signed-off-by: Michael Buesch <mb@bu3sch.de>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
This commit is contained in:
Michael Buesch 2007-12-22 22:01:36 +01:00 committed by David S. Miller
parent f3dd3fcc2c
commit 993e1c780b
2 changed files with 45 additions and 29 deletions

View file

@ -94,7 +94,6 @@ int ssb_pcmcia_switch_core(struct ssb_bus *bus,
struct ssb_device *dev) struct ssb_device *dev)
{ {
int err; int err;
unsigned long flags;
#if SSB_VERBOSE_PCMCIACORESWITCH_DEBUG #if SSB_VERBOSE_PCMCIACORESWITCH_DEBUG
ssb_printk(KERN_INFO PFX ssb_printk(KERN_INFO PFX
@ -103,11 +102,9 @@ int ssb_pcmcia_switch_core(struct ssb_bus *bus,
dev->core_index); dev->core_index);
#endif #endif
spin_lock_irqsave(&bus->bar_lock, flags);
err = ssb_pcmcia_switch_coreidx(bus, dev->core_index); err = ssb_pcmcia_switch_coreidx(bus, dev->core_index);
if (!err) if (!err)
bus->mapped_device = dev; bus->mapped_device = dev;
spin_unlock_irqrestore(&bus->bar_lock, flags);
return err; return err;
} }
@ -115,14 +112,12 @@ int ssb_pcmcia_switch_core(struct ssb_bus *bus,
int ssb_pcmcia_switch_segment(struct ssb_bus *bus, u8 seg) int ssb_pcmcia_switch_segment(struct ssb_bus *bus, u8 seg)
{ {
int attempts = 0; int attempts = 0;
unsigned long flags;
conf_reg_t reg; conf_reg_t reg;
int res, err = 0; int res;
SSB_WARN_ON((seg != 0) && (seg != 1)); SSB_WARN_ON((seg != 0) && (seg != 1));
reg.Offset = 0x34; reg.Offset = 0x34;
reg.Function = 0; reg.Function = 0;
spin_lock_irqsave(&bus->bar_lock, flags);
while (1) { while (1) {
reg.Action = CS_WRITE; reg.Action = CS_WRITE;
reg.Value = seg; reg.Value = seg;
@ -143,13 +138,11 @@ int ssb_pcmcia_switch_segment(struct ssb_bus *bus, u8 seg)
udelay(10); udelay(10);
} }
bus->mapped_pcmcia_seg = seg; bus->mapped_pcmcia_seg = seg;
out_unlock:
spin_unlock_irqrestore(&bus->bar_lock, flags); return 0;
return err;
error: error:
ssb_printk(KERN_ERR PFX "Failed to switch pcmcia segment\n"); ssb_printk(KERN_ERR PFX "Failed to switch pcmcia segment\n");
err = -ENODEV; return -ENODEV;
goto out_unlock;
} }
static int select_core_and_segment(struct ssb_device *dev, static int select_core_and_segment(struct ssb_device *dev,
@ -182,22 +175,33 @@ static int select_core_and_segment(struct ssb_device *dev,
static u16 ssb_pcmcia_read16(struct ssb_device *dev, u16 offset) static u16 ssb_pcmcia_read16(struct ssb_device *dev, u16 offset)
{ {
struct ssb_bus *bus = dev->bus; struct ssb_bus *bus = dev->bus;
unsigned long flags;
int err;
u16 value = 0xFFFF;
if (unlikely(select_core_and_segment(dev, &offset))) spin_lock_irqsave(&bus->bar_lock, flags);
return 0xFFFF; err = select_core_and_segment(dev, &offset);
if (likely(!err))
value = readw(bus->mmio + offset);
spin_unlock_irqrestore(&bus->bar_lock, flags);
return readw(bus->mmio + offset); return value;
} }
static u32 ssb_pcmcia_read32(struct ssb_device *dev, u16 offset) static u32 ssb_pcmcia_read32(struct ssb_device *dev, u16 offset)
{ {
struct ssb_bus *bus = dev->bus; struct ssb_bus *bus = dev->bus;
u32 lo, hi; unsigned long flags;
int err;
u32 lo = 0xFFFFFFFF, hi = 0xFFFFFFFF;
if (unlikely(select_core_and_segment(dev, &offset))) spin_lock_irqsave(&bus->bar_lock, flags);
return 0xFFFFFFFF; err = select_core_and_segment(dev, &offset);
lo = readw(bus->mmio + offset); if (likely(!err)) {
hi = readw(bus->mmio + offset + 2); lo = readw(bus->mmio + offset);
hi = readw(bus->mmio + offset + 2);
}
spin_unlock_irqrestore(&bus->bar_lock, flags);
return (lo | (hi << 16)); return (lo | (hi << 16));
} }
@ -205,22 +209,31 @@ static u32 ssb_pcmcia_read32(struct ssb_device *dev, u16 offset)
static void ssb_pcmcia_write16(struct ssb_device *dev, u16 offset, u16 value) static void ssb_pcmcia_write16(struct ssb_device *dev, u16 offset, u16 value)
{ {
struct ssb_bus *bus = dev->bus; struct ssb_bus *bus = dev->bus;
unsigned long flags;
int err;
if (unlikely(select_core_and_segment(dev, &offset))) spin_lock_irqsave(&bus->bar_lock, flags);
return; err = select_core_and_segment(dev, &offset);
writew(value, bus->mmio + 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) static void ssb_pcmcia_write32(struct ssb_device *dev, u16 offset, u32 value)
{ {
struct ssb_bus *bus = dev->bus; struct ssb_bus *bus = dev->bus;
unsigned long flags;
int err;
if (unlikely(select_core_and_segment(dev, &offset))) spin_lock_irqsave(&bus->bar_lock, flags);
return; err = select_core_and_segment(dev, &offset);
writeb((value & 0xFF000000) >> 24, bus->mmio + offset + 3); if (likely(!err)) {
writeb((value & 0x00FF0000) >> 16, bus->mmio + offset + 2); writew((value & 0x0000FFFF), bus->mmio + offset);
writeb((value & 0x0000FF00) >> 8, bus->mmio + offset + 1); writew(((value & 0xFFFF0000) >> 16), bus->mmio + offset + 2);
writeb((value & 0x000000FF) >> 0, bus->mmio + offset + 0); }
mmiowb();
spin_unlock_irqrestore(&bus->bar_lock, flags);
} }
/* Not "static", as it's used in main.c */ /* Not "static", as it's used in main.c */
@ -231,10 +244,12 @@ const struct ssb_bus_ops ssb_pcmcia_ops = {
.write32 = ssb_pcmcia_write32, .write32 = ssb_pcmcia_write32,
}; };
#include <linux/etherdevice.h>
int ssb_pcmcia_get_invariants(struct ssb_bus *bus, int ssb_pcmcia_get_invariants(struct ssb_bus *bus,
struct ssb_init_invariants *iv) struct ssb_init_invariants *iv)
{ {
//TODO //TODO
random_ether_addr(iv->sprom.il0mac);
return 0; return 0;
} }

View file

@ -231,7 +231,8 @@ struct ssb_bus {
struct ssb_device *mapped_device; struct ssb_device *mapped_device;
/* Currently mapped PCMCIA segment. (bustype == SSB_BUSTYPE_PCMCIA only) */ /* Currently mapped PCMCIA segment. (bustype == SSB_BUSTYPE_PCMCIA only) */
u8 mapped_pcmcia_seg; u8 mapped_pcmcia_seg;
/* Lock for core and segment switching. */ /* Lock for core and segment switching.
* On PCMCIA-host busses this is used to protect the whole MMIO access. */
spinlock_t bar_lock; spinlock_t bar_lock;
/* The bus this backplane is running on. */ /* The bus this backplane is running on. */