From 993e1c780b323736a2cdc24564f35e80ce8d3337 Mon Sep 17 00:00:00 2001 From: Michael Buesch Date: Sat, 22 Dec 2007 22:01:36 +0100 Subject: [PATCH] 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 Signed-off-by: John W. Linville --- drivers/ssb/pcmcia.c | 71 ++++++++++++++++++++++++++++++------------------- include/linux/ssb/ssb.h | 3 ++- 2 files changed, 45 insertions(+), 29 deletions(-) diff --git a/drivers/ssb/pcmcia.c b/drivers/ssb/pcmcia.c index bb44a76..46816cd 100644 --- a/drivers/ssb/pcmcia.c +++ b/drivers/ssb/pcmcia.c @@ -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 @@ -103,11 +102,9 @@ 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; } @@ -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 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; @@ -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, @@ -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) { 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)); } @@ -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) { 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 */ @@ -231,10 +244,12 @@ const struct ssb_bus_ops ssb_pcmcia_ops = { .write32 = ssb_pcmcia_write32, }; +#include int ssb_pcmcia_get_invariants(struct ssb_bus *bus, struct ssb_init_invariants *iv) { //TODO + random_ether_addr(iv->sprom.il0mac); return 0; } diff --git a/include/linux/ssb/ssb.h b/include/linux/ssb/ssb.h index 0eaa984..cacbae5 100644 --- a/include/linux/ssb/ssb.h +++ b/include/linux/ssb/ssb.h @@ -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. */ -- 2.7.4