Skip to content

Commit

Permalink
ssb: Fix PCMCIA lowlevel register access
Browse files Browse the repository at this point in the history
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 <[email protected]>
Signed-off-by: John W. Linville <[email protected]>
  • Loading branch information
Michael Buesch authored and davem330 committed Jan 28, 2008
1 parent f3dd3fc commit 993e1c7
Show file tree
Hide file tree
Showing 2 changed files with 45 additions and 29 deletions.
71 changes: 43 additions & 28 deletions drivers/ssb/pcmcia.c
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,6 @@ int ssb_pcmcia_switch_core(struct ssb_bus *bus,
struct ssb_device *dev)
{
int err;
unsigned long flags;

#if SSB_VERBOSE_PCMCIACORESWITCH_DEBUG
ssb_printk(KERN_INFO PFX
Expand All @@ -103,26 +102,22 @@ int ssb_pcmcia_switch_core(struct ssb_bus *bus,
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;
}

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;

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;
Expand All @@ -143,13 +138,11 @@ 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 int select_core_and_segment(struct ssb_device *dev,
Expand Down Expand Up @@ -182,45 +175,65 @@ static int select_core_and_segment(struct ssb_device *dev,
static u16 ssb_pcmcia_read16(struct ssb_device *dev, u16 offset)
{
struct ssb_bus *bus = dev->bus;
unsigned long flags;
int err;
u16 value = 0xFFFF;

if (unlikely(select_core_and_segment(dev, &offset)))
return 0xFFFF;
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 readw(bus->mmio + offset);
return value;
}

static u32 ssb_pcmcia_read32(struct ssb_device *dev, u16 offset)
{
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)))
return 0xFFFFFFFF;
lo = readw(bus->mmio + offset);
hi = readw(bus->mmio + offset + 2);
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);

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(select_core_and_segment(dev, &offset)))
return;
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(select_core_and_segment(dev, &offset)))
return;
writeb((value & 0xFF000000) >> 24, bus->mmio + offset + 3);
writeb((value & 0x00FF0000) >> 16, bus->mmio + offset + 2);
writeb((value & 0x0000FF00) >> 8, bus->mmio + offset + 1);
writeb((value & 0x000000FF) >> 0, bus->mmio + offset + 0);
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 */
Expand All @@ -231,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;
}

Expand Down
3 changes: 2 additions & 1 deletion include/linux/ssb/ssb.h
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,8 @@ struct ssb_bus {
struct ssb_device *mapped_device;
/* Currently mapped PCMCIA segment. (bustype == SSB_BUSTYPE_PCMCIA only) */
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;

/* The bus this backplane is running on. */
Expand Down

0 comments on commit 993e1c7

Please sign in to comment.