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:
parent
f3dd3fcc2c
commit
993e1c780b
2 changed files with 45 additions and 29 deletions
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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. */
|
||||||
|
|
Loading…
Reference in a new issue