ssb: Add support for block-I/O
authorMichael Buesch <mb@bu3sch.de>
Wed, 2 Apr 2008 17:46:56 +0000 (19:46 +0200)
committerJohn W. Linville <linville@tuxdriver.com>
Tue, 8 Apr 2008 20:44:40 +0000 (16:44 -0400)
This adds support for block based I/O to SSB.
This is needed in order to efficiently support PIO data
transfers to the card.
The block-I/O support is only compiled, if it's selected by the
weird driver that needs it. So there's no overhead for sane devices.

Signed-off-by: Michael Buesch <mb@bu3sch.de>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
drivers/ssb/Kconfig
drivers/ssb/main.c
drivers/ssb/pci.c
drivers/ssb/pcmcia.c
include/linux/ssb/ssb.h

index 0f7cce2560d19ccb4dea8799bcc2fe74373d8d0d..cd845b8acd17b006ac4021120f0aee8bec85f045 100644 (file)
@@ -24,6 +24,11 @@ config SSB
 config SSB_SPROM
        bool
 
+# Support for Block-I/O. SELECT this from the driver that needs it.
+config SSB_BLOCKIO
+       bool
+       depends on SSB
+
 config SSB_PCIHOST_POSSIBLE
        bool
        depends on SSB && (PCI = y || PCI = SSB)
index 3e58db7ef6088e50f41a22be76d421c425fd0dc9..19ddd2bd1057119136dfa1f86abac79e36559c24 100644 (file)
@@ -555,6 +555,55 @@ static u32 ssb_ssb_read32(struct ssb_device *dev, u16 offset)
        return readl(bus->mmio + offset);
 }
 
+#ifdef CONFIG_SSB_BLOCKIO
+static void ssb_ssb_block_read(struct ssb_device *dev, void *buffer,
+                              size_t count, u16 offset, u8 reg_width)
+{
+       struct ssb_bus *bus = dev->bus;
+       void __iomem *addr;
+
+       offset += dev->core_index * SSB_CORE_SIZE;
+       addr = bus->mmio + offset;
+
+       switch (reg_width) {
+       case sizeof(u8): {
+               u8 *buf = buffer;
+
+               while (count) {
+                       *buf = __raw_readb(addr);
+                       buf++;
+                       count--;
+               }
+               break;
+       }
+       case sizeof(u16): {
+               __le16 *buf = buffer;
+
+               SSB_WARN_ON(count & 1);
+               while (count) {
+                       *buf = (__force __le16)__raw_readw(addr);
+                       buf++;
+                       count -= 2;
+               }
+               break;
+       }
+       case sizeof(u32): {
+               __le32 *buf = buffer;
+
+               SSB_WARN_ON(count & 3);
+               while (count) {
+                       *buf = (__force __le32)__raw_readl(addr);
+                       buf++;
+                       count -= 4;
+               }
+               break;
+       }
+       default:
+               SSB_WARN_ON(1);
+       }
+}
+#endif /* CONFIG_SSB_BLOCKIO */
+
 static void ssb_ssb_write8(struct ssb_device *dev, u16 offset, u8 value)
 {
        struct ssb_bus *bus = dev->bus;
@@ -579,6 +628,55 @@ static void ssb_ssb_write32(struct ssb_device *dev, u16 offset, u32 value)
        writel(value, bus->mmio + offset);
 }
 
+#ifdef CONFIG_SSB_BLOCKIO
+static void ssb_ssb_block_write(struct ssb_device *dev, const void *buffer,
+                               size_t count, u16 offset, u8 reg_width)
+{
+       struct ssb_bus *bus = dev->bus;
+       void __iomem *addr;
+
+       offset += dev->core_index * SSB_CORE_SIZE;
+       addr = bus->mmio + offset;
+
+       switch (reg_width) {
+       case sizeof(u8): {
+               const u8 *buf = buffer;
+
+               while (count) {
+                       __raw_writeb(*buf, addr);
+                       buf++;
+                       count--;
+               }
+               break;
+       }
+       case sizeof(u16): {
+               const __le16 *buf = buffer;
+
+               SSB_WARN_ON(count & 1);
+               while (count) {
+                       __raw_writew((__force u16)(*buf), addr);
+                       buf++;
+                       count -= 2;
+               }
+               break;
+       }
+       case sizeof(u32): {
+               const __le32 *buf = buffer;
+
+               SSB_WARN_ON(count & 3);
+               while (count) {
+                       __raw_writel((__force u32)(*buf), addr);
+                       buf++;
+                       count -= 4;
+               }
+               break;
+       }
+       default:
+               SSB_WARN_ON(1);
+       }
+}
+#endif /* CONFIG_SSB_BLOCKIO */
+
 /* Ops for the plain SSB bus without a host-device (no PCI or PCMCIA). */
 static const struct ssb_bus_ops ssb_ssb_ops = {
        .read8          = ssb_ssb_read8,
@@ -587,6 +685,10 @@ static const struct ssb_bus_ops ssb_ssb_ops = {
        .write8         = ssb_ssb_write8,
        .write16        = ssb_ssb_write16,
        .write32        = ssb_ssb_write32,
+#ifdef CONFIG_SSB_BLOCKIO
+       .block_read     = ssb_ssb_block_read,
+       .block_write    = ssb_ssb_block_write,
+#endif
 };
 
 static int ssb_fetch_invariants(struct ssb_bus *bus,
index f1514b33cfae442ecdce8702aec55654a41dd2ed..904b1a8d088590bfc51d6d10935d35ce553b1a1c 100644 (file)
@@ -613,6 +613,41 @@ static u32 ssb_pci_read32(struct ssb_device *dev, u16 offset)
        return ioread32(bus->mmio + offset);
 }
 
+#ifdef CONFIG_SSB_BLOCKIO
+static void ssb_pci_block_read(struct ssb_device *dev, void *buffer,
+                              size_t count, u16 offset, u8 reg_width)
+{
+       struct ssb_bus *bus = dev->bus;
+       void __iomem *addr = bus->mmio + offset;
+
+       if (unlikely(ssb_pci_assert_buspower(bus)))
+               goto error;
+       if (unlikely(bus->mapped_device != dev)) {
+               if (unlikely(ssb_pci_switch_core(bus, dev)))
+                       goto error;
+       }
+       switch (reg_width) {
+       case sizeof(u8):
+               ioread8_rep(addr, buffer, count);
+               break;
+       case sizeof(u16):
+               SSB_WARN_ON(count & 1);
+               ioread16_rep(addr, buffer, count >> 1);
+               break;
+       case sizeof(u32):
+               SSB_WARN_ON(count & 3);
+               ioread32_rep(addr, buffer, count >> 2);
+               break;
+       default:
+               SSB_WARN_ON(1);
+       }
+
+       return;
+error:
+       memset(buffer, 0xFF, count);
+}
+#endif /* CONFIG_SSB_BLOCKIO */
+
 static void ssb_pci_write8(struct ssb_device *dev, u16 offset, u8 value)
 {
        struct ssb_bus *bus = dev->bus;
@@ -652,6 +687,37 @@ static void ssb_pci_write32(struct ssb_device *dev, u16 offset, u32 value)
        iowrite32(value, bus->mmio + offset);
 }
 
+#ifdef CONFIG_SSB_BLOCKIO
+static void ssb_pci_block_write(struct ssb_device *dev, const void *buffer,
+                               size_t count, u16 offset, u8 reg_width)
+{
+       struct ssb_bus *bus = dev->bus;
+       void __iomem *addr = bus->mmio + offset;
+
+       if (unlikely(ssb_pci_assert_buspower(bus)))
+               return;
+       if (unlikely(bus->mapped_device != dev)) {
+               if (unlikely(ssb_pci_switch_core(bus, dev)))
+                       return;
+       }
+       switch (reg_width) {
+       case sizeof(u8):
+               iowrite8_rep(addr, buffer, count);
+               break;
+       case sizeof(u16):
+               SSB_WARN_ON(count & 1);
+               iowrite16_rep(addr, buffer, count >> 1);
+               break;
+       case sizeof(u32):
+               SSB_WARN_ON(count & 3);
+               iowrite32_rep(addr, buffer, count >> 2);
+               break;
+       default:
+               SSB_WARN_ON(1);
+       }
+}
+#endif /* CONFIG_SSB_BLOCKIO */
+
 /* Not "static", as it's used in main.c */
 const struct ssb_bus_ops ssb_pci_ops = {
        .read8          = ssb_pci_read8,
@@ -660,6 +726,10 @@ const struct ssb_bus_ops ssb_pci_ops = {
        .write8         = ssb_pci_write8,
        .write16        = ssb_pci_write16,
        .write32        = ssb_pci_write32,
+#ifdef CONFIG_SSB_BLOCKIO
+       .block_read     = ssb_pci_block_read,
+       .block_write    = ssb_pci_block_write,
+#endif
 };
 
 static ssize_t ssb_pci_attr_sprom_show(struct device *pcidev,
index dcaf2412bea752ec117b4e1d3c808b16913e585d..24c2a46c14760cbb879f1e367aff8774e01ac7b2 100644 (file)
@@ -285,6 +285,64 @@ static u32 ssb_pcmcia_read32(struct ssb_device *dev, u16 offset)
        return (lo | (hi << 16));
 }
 
+#ifdef CONFIG_SSB_BLOCKIO
+static void ssb_pcmcia_block_read(struct ssb_device *dev, void *buffer,
+                                 size_t count, u16 offset, u8 reg_width)
+{
+       struct ssb_bus *bus = dev->bus;
+       unsigned long flags;
+       void __iomem *addr = bus->mmio + offset;
+       int err;
+
+       spin_lock_irqsave(&bus->bar_lock, flags);
+       err = select_core_and_segment(dev, &offset);
+       if (unlikely(err)) {
+               memset(buffer, 0xFF, count);
+               goto unlock;
+       }
+       switch (reg_width) {
+       case sizeof(u8): {
+               u8 *buf = buffer;
+
+               while (count) {
+                       *buf = __raw_readb(addr);
+                       buf++;
+                       count--;
+               }
+               break;
+       }
+       case sizeof(u16): {
+               __le16 *buf = buffer;
+
+               SSB_WARN_ON(count & 1);
+               while (count) {
+                       *buf = (__force __le16)__raw_readw(addr);
+                       buf++;
+                       count -= 2;
+               }
+               break;
+       }
+       case sizeof(u32): {
+               __le16 *buf = buffer;
+
+               SSB_WARN_ON(count & 3);
+               while (count) {
+                       *buf = (__force __le16)__raw_readw(addr);
+                       buf++;
+                       *buf = (__force __le16)__raw_readw(addr + 2);
+                       buf++;
+                       count -= 4;
+               }
+               break;
+       }
+       default:
+               SSB_WARN_ON(1);
+       }
+unlock:
+       spin_unlock_irqrestore(&bus->bar_lock, flags);
+}
+#endif /* CONFIG_SSB_BLOCKIO */
+
 static void ssb_pcmcia_write8(struct ssb_device *dev, u16 offset, u8 value)
 {
        struct ssb_bus *bus = dev->bus;
@@ -329,6 +387,63 @@ static void ssb_pcmcia_write32(struct ssb_device *dev, u16 offset, u32 value)
        spin_unlock_irqrestore(&bus->bar_lock, flags);
 }
 
+#ifdef CONFIG_SSB_BLOCKIO
+static void ssb_pcmcia_block_write(struct ssb_device *dev, const void *buffer,
+                                  size_t count, u16 offset, u8 reg_width)
+{
+       struct ssb_bus *bus = dev->bus;
+       unsigned long flags;
+       void __iomem *addr = bus->mmio + offset;
+       int err;
+
+       spin_lock_irqsave(&bus->bar_lock, flags);
+       err = select_core_and_segment(dev, &offset);
+       if (unlikely(err))
+               goto unlock;
+       switch (reg_width) {
+       case sizeof(u8): {
+               const u8 *buf = buffer;
+
+               while (count) {
+                       __raw_writeb(*buf, addr);
+                       buf++;
+                       count--;
+               }
+               break;
+       }
+       case sizeof(u16): {
+               const __le16 *buf = buffer;
+
+               SSB_WARN_ON(count & 1);
+               while (count) {
+                       __raw_writew((__force u16)(*buf), addr);
+                       buf++;
+                       count -= 2;
+               }
+               break;
+       }
+       case sizeof(u32): {
+               const __le16 *buf = buffer;
+
+               SSB_WARN_ON(count & 3);
+               while (count) {
+                       __raw_writew((__force u16)(*buf), addr);
+                       buf++;
+                       __raw_writew((__force u16)(*buf), addr + 2);
+                       buf++;
+                       count -= 4;
+               }
+               break;
+       }
+       default:
+               SSB_WARN_ON(1);
+       }
+unlock:
+       mmiowb();
+       spin_unlock_irqrestore(&bus->bar_lock, flags);
+}
+#endif /* CONFIG_SSB_BLOCKIO */
+
 /* Not "static", as it's used in main.c */
 const struct ssb_bus_ops ssb_pcmcia_ops = {
        .read8          = ssb_pcmcia_read8,
@@ -337,6 +452,10 @@ const struct ssb_bus_ops ssb_pcmcia_ops = {
        .write8         = ssb_pcmcia_write8,
        .write16        = ssb_pcmcia_write16,
        .write32        = ssb_pcmcia_write32,
+#ifdef CONFIG_SSB_BLOCKIO
+       .block_read     = ssb_pcmcia_block_read,
+       .block_write    = ssb_pcmcia_block_write,
+#endif
 };
 
 static int ssb_pcmcia_sprom_command(struct ssb_bus *bus, u8 command)
index a8ca396f810a601adf206b99576c3f500437ea53..9f95afd0e9e35601555bde8fa273a19a333259c8 100644 (file)
@@ -78,6 +78,12 @@ struct ssb_bus_ops {
        void (*write8)(struct ssb_device *dev, u16 offset, u8 value);
        void (*write16)(struct ssb_device *dev, u16 offset, u16 value);
        void (*write32)(struct ssb_device *dev, u16 offset, u32 value);
+#ifdef CONFIG_SSB_BLOCKIO
+       void (*block_read)(struct ssb_device *dev, void *buffer,
+                          size_t count, u16 offset, u8 reg_width);
+       void (*block_write)(struct ssb_device *dev, const void *buffer,
+                           size_t count, u16 offset, u8 reg_width);
+#endif
 };
 
 
@@ -374,6 +380,19 @@ static inline void ssb_write32(struct ssb_device *dev, u16 offset, u32 value)
 {
        dev->ops->write32(dev, offset, value);
 }
+#ifdef CONFIG_SSB_BLOCKIO
+static inline void ssb_block_read(struct ssb_device *dev, void *buffer,
+                                 size_t count, u16 offset, u8 reg_width)
+{
+       dev->ops->block_read(dev, buffer, count, offset, reg_width);
+}
+
+static inline void ssb_block_write(struct ssb_device *dev, const void *buffer,
+                                  size_t count, u16 offset, u8 reg_width)
+{
+       dev->ops->block_write(dev, buffer, count, offset, reg_width);
+}
+#endif /* CONFIG_SSB_BLOCKIO */
 
 
 /* Translation (routing) bits that need to be ORed to DMA