Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d625a29b authored by Michael Buesch's avatar Michael Buesch Committed by John W. Linville
Browse files

ssb: Add support for block-I/O



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: default avatarMichael Buesch <mb@bu3sch.de>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent 93af2614
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -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)
+102 −0
Original line number Diff line number Diff line
@@ -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,
+70 −0
Original line number Diff line number Diff line
@@ -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,
+119 −0
Original line number Diff line number Diff line
@@ -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)
+19 −0
Original line number Diff line number Diff line
@@ -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